阿里云短信验证码服务

文章目录

环境

  • 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/
相关推荐
Serverless社区14 小时前
AgentRun 实战:快速构建 AI 舆情实时分析专家
阿里云·云原生·serverless·函数计算
咕噜企业分发小米16 小时前
阿里云AI教育产品如何助力企业提升客户粘性?
人工智能·microsoft·阿里云
翼龙云_cloud17 小时前
阿里云渠道商:阿里云自动扩缩容配置教程
运维·服务器·阿里云·云计算
小毛驴85019 小时前
Maven同时配置阿里云仓库和私有仓库
java·阿里云·maven
咕噜企业分发小米21 小时前
阿里云AI教育产品如何助力老年大学提高教学质量?
人工智能·阿里云·云计算
咕噜企业分发小米1 天前
阿里云与华为云AI教育生态重构中,企业如何参与?
人工智能·阿里云·华为云
咕噜企业分发小米1 天前
阿里云和华为云在AI教育领域的生态重构具体会带来哪些影响?
人工智能·阿里云·华为云
数据库知识分享者小北2 天前
免费体验《自建 MySQL 迁移至 PolarDB 分布式 V2.0》
数据库·分布式·mysql·阿里云·云原生·polardb
咕噜企业分发小米2 天前
阿里云AI教育产品如何助力老年教育?
人工智能·阿里云·云计算
咕噜企业分发小米2 天前
阿里云与华为云AI教育产品有哪些未来发展规划?
人工智能·阿里云·华为云