文章目录
环境
- Windows 11 家庭版
- PHP 8.2.12
背景
我们在日常生活中,经常会用到手机验证码短信。它可以大大提高安全性,证明当前操作的人确实是手机号的合法所有者,并且能防止批量自动化攻击。
本文以阿里云短信服务为例,介绍如何实现发送手机验证码短信。
方法
阿里云提供了短信服务(当然要花钱买),首先要获取以下信息:
- accessKeyId:
- accessKeySecret:
- SignName:短信的发送方(比如公司名称)
- TemplateCode:短信内容模板编号,类似
SMS_xxxxxx
应用需要安装阿里云SDK,才能调用阿里云的短信服务,下面以PHP为例介绍具体方法。
alibabacloud/dysmsapi-20170525
新建目录 test0821_1
,进入该目录(即项目根目录)。
首先要安装阿里云SDK的依赖包,运行:
powershell
composer require alibabacloud/dysmsapi-20170525 4.1.2
注:这里指定了版本 4.1.2
,经测试,假如不指定版本,实际也是 4.1.2
(在 composer.lock
文件里查看)。
在项目根目录下创建 test.php
文件:
php
<?php
require_once __DIR__ . '/vendor/autoload.php';
use AlibabaCloud\SDK\Dysmsapi\V20170525\Dysmsapi;
use AlibabaCloud\SDK\Dysmsapi\V20170525\Models\SendSmsRequest;
use AlibabaCloud\Dara\Models\RuntimeOptions;
use Darabonba\OpenApi\Models\Config as DysmsapiConfig;
use Exception;
$accessKeyId = 'xxxxxx';
$accessKeySecret = 'xxxxxx';
try {
$config = new DysmsapiConfig([
"accessKeyId" => $accessKeyId,
"accessKeySecret" => $accessKeySecret,
"endpoint" => "dysmsapi.aliyuncs.com" // 显式设置endpoint
]);
$client = new Dysmsapi($config);
$request = new SendSmsRequest([
"phoneNumbers" => 'xxxxxx',
"signName" => 'xxxxxx',
"templateCode" => 'xxxxxx',
"templateParam" => json_encode(['code' => '555666'])
]);
// 设置运行时选项
$runtime = new RuntimeOptions([]);
$runtime->ignoreSSL = true; // 未配置CA证书,临时忽略SSL验证
// 发送短信
$response = $client->sendSmsWithOptions($request, $runtime);
if ($response->body->code === 'OK') {
echo '发送成功!BizId: ' . $response->body->bizId . PHP_EOL;
} else {
echo "发送失败: " . $response->body->message . PHP_EOL;
}
} catch (Exception $e) {
echo $e->getMessage() . PHP_EOL;
}
注意:代码中有几处 xxxxxx
,需要替换成真实值:
- accessKeyId:
- accessKeySecret:
- PhoneNumbers:短信的接收方(手机号)
- SignName:短信的发送方(比如公司名称)
- TemplateCode:比如
SMS_xxxxxx
,这个东东是短信内容的模板,只不过有一些参数需要填充。本例中是一个发送验证码的短信,模板内容是:"您的验证码为:xxxxxx,请勿泄露于他人!",其中的参数通过TemplateParam
来填充。
运行程序,就会给目标手机发送一条短信:

注:本例只是一个demo, accessKeyId
和 accessKeySecret
以明文形式直接放在了代码里。实际项目中,一般可以放到Secret里。
可见,验证码短信是短信模板 + 验证码,其中验证码的生成和校验,都是应用后端的行为,和阿里云以及运营商并没有关系。
- 验证码生成:本例中是应用后端hardcode了一个验证码,实际运行时,可以随机产生6位数字,然后要把它暂存起来,以便待会儿校验
- 验证码校验:用户收到验证码短信后,在应用里输入验证码,然后提交,后端拿到验证码后,和保存的验证码相比较,如果匹配,则校验成功,说明用户提供的手机号是真实有效的。
另外,阿里云网站上( https://next.api.aliyun.com/api-tools/sdk/Dysmsapi?version=2017-05-25&language=php-tea&tab=primer-doc
)提供的是一个类,经改编后如下:
php
<?php
require_once __DIR__ . '/vendor/autoload.php';
use AlibabaCloud\SDK\Dysmsapi\V20170525\Dysmsapi;
use AlibabaCloud\SDK\Dysmsapi\V20170525\Models\SendSmsRequest;
use AlibabaCloud\Dara\Models\RuntimeOptions;
use AlibabaCloud\Tea\Exception\TeaError;
use Darabonba\OpenApi\Models\Config as DysmsapiConfig;
use Exception;
class AliyunSmsService {
private $accessKeyId;
private $accessKeySecret;
private $client;
public function __construct($accessKeyId, $accessKeySecret) {
$this->accessKeyId = $accessKeyId;
$this->accessKeySecret = $accessKeySecret;
$this->initializeClient();
}
private function initializeClient() {
try {
$config = new DysmsapiConfig([
"accessKeyId" => $this->accessKeyId,
"accessKeySecret" => $this->accessKeySecret,
"endpoint" => "dysmsapi.aliyuncs.com" // 显式设置endpoint
]);
$this->client = new Dysmsapi($config);
} catch (Exception $e) {
throw new Exception("初始化短信客户端失败: " . $e->getMessage());
}
}
public function sendSms($phoneNumber, $signName, $templateCode, $templateParams = []) {
if (empty($phoneNumber) || empty($signName) || empty($templateCode)) {
return [
'success' => false,
'message' => '参数不完整'
];
}
try {
// 准备请求参数
$request = new SendSmsRequest([
"phoneNumbers" => $phoneNumber,
"signName" => $signName,
"templateCode" => $templateCode,
"templateParam" => !empty($templateParams) ? json_encode($templateParams, JSON_UNESCAPED_UNICODE) : null
]);
// 设置运行时选项
$runtime = new RuntimeOptions([]);
$runtime->ignoreSSL = true; // 未配置CA证书,临时忽略SSL验证
// 发送短信
$response = $this->client->sendSmsWithOptions($request, $runtime);
// 处理响应
if ($response->body->code === 'OK') {
return [
'success' => true,
'message' => '发送成功',
'bizId' => $response->body->bizId,
'requestId' => $response->body->requestId
];
} else {
return [
'success' => false,
'message' => $response->body->message,
'code' => $response->body->code,
'requestId' => $response->body->requestId
];
}
} catch (TeaError $e) {
return [
'success' => false,
'message' => 'TeaError: ' . $e->getMessage(),
'code' => $e->getCode()
];
} catch (Exception $e) {
return [
'success' => false,
'message' => 'Exception: ' . $e->getMessage(),
'code' => $e->getCode()
];
}
}
}
// ==================== 使用示例 ====================
// 配置信息 - 请替换为实际值
$config = [
'accessKeyId' => 'xxxxxx',
'accessKeySecret' => 'xxxxxx',
'phoneNumber' => 'xxxxxx',
'signName' => 'xxxxxx', // 如:阿里云
'templateCode' => 'xxxxxx', // 模板CODE
];
try {
// 创建实例
$smsService = new AliyunSmsService($config['accessKeyId'], $config['accessKeySecret']);
// 生成验证码
$verificationCode = '234567';
// 发送短信
$result = $smsService->sendSms(
$config['phoneNumber'],
$config['signName'],
$config['templateCode'],
['code' => $verificationCode] // 模板参数,根据实际模板调整
);
// 输出结果
echo "发送结果: " . ($result['success'] ? '成功' : '失败') . "\n";
echo "消息: " . $result['message'] . "\n";
if ($result['success']) {
echo "业务ID: " . $result['bizId'] . "\n";
// 这里应该将验证码存储到Session或Redis中
session_start();
$_SESSION['sms_verification'] = [
'code' => $verificationCode,
'phone' => $config['phoneNumber'],
'expire_time' => time() + 300 // 5分钟过期
];
} else {
echo "错误代码: " . ($result['code'] ?? '未知') . "\n";
}
} catch (Exception $e) {
echo "初始化失败: " . $e->getMessage() . "\n";
}
// 打印详细结果(调试用)
echo "\n详细响应:\n";
print_r($result);
注:官网上的代码,貌似无法直接使用,有各种小毛病。上面的代码是结合官网和DeepSeek改编的。
alibabacloud/sdk (todo)
新建目录 test0821_2
,进入该目录(即项目根目录)。
首先要安装阿里云SDK的依赖包,运行:
powershell
composer require alibabacloud/sdk
然后代码和前面类似,但是我还没有搞定。。。总是各种报错。
alibabacloud/client
新建目录 test0821_3
,进入该目录,(即项目根目录)。
首先要安装阿里云SDK的依赖包,运行:
powershell
composer require alibabacloud/client
在项目根目录下创建 test.php
:
php
<?php
require_once __DIR__ . '/vendor/autoload.php';
use AlibabaCloud\Client\AlibabaCloud;
use AlibabaCloud\Client\Exception\ClientException;
use AlibabaCloud\Client\Exception\ServerException;
// 配置 AccessKey
$accessKeyId = 'xxxxxx'; //阿里云短信获取的accessKeyId
$accessKeySecret = 'xxxxxx'; //阿里云短信获取的accessKeySecret
try {
// 初始化客户端
AlibabaCloud::accessKeyClient($accessKeyId, $accessKeySecret)
->regionId('cn-hangzhou') // 默认即可
->asDefaultClient();
// 发送短信
$result = AlibabaCloud::rpc()
->product('Dysmsapi')
->version('2017-05-25')
->action('SendSms')
->method('POST')
->host('dysmsapi.aliyuncs.com')
->options([
'query' => [
'RegionId' => "cn-hangzhou",
'PhoneNumbers' => "xxxxxx",
'SignName' => "xxxxxx",
'TemplateCode' => "xxxxxx",
'TemplateParam' => json_encode(['code' => '123456']),
],
])
->request();
// 处理结果
$result = $result->toArray();
if ($result['Code'] === 'OK') {
echo "发送成功!BizId: " . $result['BizId'];
} else {
echo "发送失败: " . $result['Message'];
}
} catch (ClientException $e) {
echo $e->getErrorMessage() . PHP_EOL;
} catch (ServerException $e) {
echo $e->getErrorMessage() . PHP_EOL;
}
注:DeepSeek说,这个方法使用的是阿里云旧版SDK,但仍然有很多应用场景,较为稳定。
参考
https://next.api.aliyun.com/api-tools/sdk/Dysmsapi
https://next.api.aliyun.com/api/Dysmsapi/2017-05-25/SendSms
https://help.aliyun.com/zh/sms/