準備工作
- 註冊 容聯云通訊 開發者賬號
- 創建應用,獲取:
Account SID、Auth Token、App ID
- 配置短信模板(如驗證碼模板 ID=1,內容:
您的驗證碼是{1},{2}分鐘內有效)
- 測試環境需在控制台添加測試手機號
目錄結構
project/
├── includes/
│ └── rly_message/
│ ├── rest.app.php # SDK 核心類
│ └── test.app.php # 控制器示例
└── templates/
└── test.html # 前端頁面
核心 SDK (rest.app.php)
<?php
class RestApp {
private $AccountSid, $AccountToken, $AppId;
private $ServerIP = 'app.cloopen.com', $ServerPort = '8883', $SoftVersion = '2013-12-26';
private $Batch, $BodyType = 'json', $enabeLog = true, $Filename = __DIR__.'/sms.log';
public function __construct($ServerIP, $ServerPort, $SoftVersion) {
$this->ServerIP = $ServerIP;
$this->ServerPort = $ServerPort;
$this->SoftVersion = $SoftVersion;
$this->Batch = date('YmdHis');
$this->Handle = fopen($this->Filename, 'a');
}
public function setAccount($sid, $token) {
$this->AccountSid = $sid; $this->AccountToken = $token;
}
public function setAppId($appId) { $this->AppId = $appId; }
/** 發送模板短信
* @param string $to 手機號,多號碼英文逗號分隔
* @param array $datas 模板變量,如 ['123456', '5']
* @param string $tempId 模板 ID
*/
public function sendTemplateSMS($to, array $datas, $tempId) {
// 1. 參數校驗
if (!$this->AccountSid || !$this->AccountToken || !$this->AppId) {
return (object)['statusCode'=>'172006', 'statusMsg'=>'配置缺失'];
}
// 2. 構建請求體
$dataStr = '';
foreach ($datas as $v) $dataStr .= "$v";
$body = "<TemplateSMS><to>$to</to><appId>$this->AppId</appId><templateId>$tempId</templateId><datas>$dataStr</datas></TemplateSMS>";
// 3. 鑒權簽名
$sig = strtoupper(md5($this->AccountSid . $this->AccountToken . $this->Batch));
$url = "https://$this->ServerIP:$this->ServerPort/$this->SoftVersion/Accounts/$this->AccountSid/SMS/TemplateSMS?sig=$sig";
$auth = base64_encode("$this->AccountSid:$this->Batch");
$headers = [
"Accept:application/$this->BodyType",
"Content-Type:application/$this->BodyType;charset=utf-8",
"Authorization:$auth"
];
// 4. 發送請求
$result = $this->curlPost($url, $body, $headers);
$this->log("REQUEST: $url\n$body\nRESPONSE: $result");
// 5. 解析響應
$obj = simplexml_load_string(trim($result));
if ($obj && $obj->statusCode == '0') {
$obj->TemplateSMS = $obj->templateSMS; // 統一屬性名
}
return $obj;
}
private function curlPost($url, $data, $headers) {
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $data,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_TIMEOUT => 30,
]);
$ret = curl_exec($ch);
if ($ret === false) {
$ret = '<?xml version="1.0"?><Response><statusCode>172001</statusCode><statusMsg>網絡錯誤</statusMsg></Response>';
}
curl_close($ch);
return $ret;
}
private function log($msg) {
if ($this->enabeLog) fwrite($this->Handle, date('Y-m-d H:i:s')." $msg\n");
}
}
控制器示例 (test.app.php)
<?php
require_once __DIR__.'/rest.app.php';
$accountSid = 'YOUR_ACCOUNT_SID';
$accountToken = 'YOUR_AUTH_TOKEN';
$appId = 'YOUR_APP_ID';
$serverIP = 'app.cloopen.com'; // 生產環境
$serverPort = '8883';
$softVersion = '2013-12-26';
$phone = $_GET['phone'] ?? '';
if (!$phone) exit(json_encode(['code'=>400, 'msg'=>'手機號不能為空']));
$rest = new RestApp($serverIP, $serverPort, $softVersion);
$rest->setAccount($accountSid, $accountToken);
$rest->setAppId($appId);
// 模板 ID=1,變量:驗證碼、有效分鐘數
$code = rand(100000, 999999);
$result = $rest->sendTemplateSMS($phone, [$code, '5'], '1');
header('Content-Type: application/json; charset=utf-8');
if ($result->statusCode == '0') {
// TODO: 將 $code 存入 Redis/Session,設置 5 分鐘過期,供驗證接口比對
echo json_encode(['code'=>200, 'msg'=>'發送成功', 'sid'=>$result->TemplateSMS->smsMessageSid]);
} else {
echo json_encode(['code'=>500, 'msg'=>$result->statusMsg, 'errCode'=>$result->statusCode]);
}
前端交互 (test.html + JS)
<!DOCTYPE html><html><head><meta charset="utf-8"><title>短信驗證碼演示</title></head><body>
<div style="max-width:400px;margin:40px auto;padding:20px;border:1px solid #eee;border-radius:8px">
<h3 style="text-align:center">短信驗證碼</h3>
<div><input type="tel" id="phone" placeholder="請輸入手機號" style="width:70%">
<button id="btnSend" style="width:28%">獲取驗證碼</button></div>
<div style="margin-top:15px"><input type="text" id="code" placeholder="輸入驗證碼" style="width:70%"></div>
<div style="margin-top:15px;text-align:center"><button id="btnVerify">驗證</button></div>
<p id="tip" style="color:#666;text-align:center;margin-top:10px"></p>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script>
let countdown = 0;
$('#btnSend').click(function(){
const phone = $('#phone').val().trim();
if (!/^1[3-9]\d{9}$/.test(phone)) return alert('手機號格式錯誤');
$(this).prop('disabled', true);
$.getJSON('test.app.php', {phone}, function(res){
if (res.code === 200) {
countdown = 60;
const timer = setInterval(() => {
$('#btnSend').text(countdown + 's 重發');
if (--countdown <= 0) { clearInterval(timer); $('#btnSend').prop('disabled', false).text('獲取驗證碼'); }
}, 1000);
$('#tip').css('color','green').text('驗證碼已發送,請查收');
} else {
$('#btnSend').prop('disabled', false);
$('#tip').css('color','red').text(res.msg);
}
});
});
$('#btnVerify').click(function(){
const code = $('#code').val().trim();
if (!code) return alert('請輸入驗證碼');
// 此處應調用後端驗證接口比對 Redis/Session 中的碼
alert('演示模式:假設驗證通過,實際請對接後端驗證邏輯');
});
</script>
</body></html>
生產環境關鍵補充
- 驗證碼存儲:Redis
SET sms:code:{phone} {code} EX 300,驗證時 GET 比對並 DEL 防暴力破解。
- 頻率限制:同一 IP/手機號 60s 內僅允許 1 次,Redis
SET sms:lock:{phone} 1 EX 60 NX。
- 異常重試:網絡錯誤(172001)可指數退避重試 3 次,記錄失敗日誌告警。
- HTTPS 證書:生產環境請配置
CURLOPT_CAINFO 指向有效 CA 憑證,勿關閉驗證。
- 模板變量順序:嚴格按模板佔位符順序傳遞
$datas 數組。