阿里云短信验证码服务

文章目录

环境

  • 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, accessKeyIdaccessKeySecret 以明文形式直接放在了代码里。实际项目中,一般可以放到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/
相关推荐
创思通信14 小时前
4G模块 EC200通过MQTT协议连接到阿里云
数据库·物联网·mqtt·阿里云·at·ec200a
蓝黑20201 天前
VSCode远程连接阿里云ECS服务器
服务器·vscode·阿里云
蓝黑20202 天前
阿里云ECS服务器搭建ThinkPHP环境
服务器·阿里云·thinkphp
tanxiaomi3 天前
阿里云 OSS 前端直传实战:表单上传 + Policy 模式详解
前端·阿里云·云计算
曾哥嵌入式4 天前
Stm32通过ESP8266 WiFi连接阿里云平台
stm32·嵌入式硬件·阿里云
阿里云云原生4 天前
用通义灵码渐进式开发 0->1 实现高考志愿规划项目题文档
阿里云·高考·通义灵码
智慧源点4 天前
阿里云RDS MySQL数据归档全攻略:方案选择指南
阿里云·云计算
m0_748254094 天前
阿里云详解:与 AWS、GCP 的全方位比较
阿里云·云计算·aws
阿里云大数据AI技术4 天前
鹰角网络基于阿里云 EMR Serverless StarRocks 的实时分析工程实践
starrocks·clickhouse·阿里云·emr·实时分析