ThinkPHP项目短信验证码发送

wen PHP项目 3

本文目录导读:

ThinkPHP项目短信验证码发送

  1. 配置短信服务商
  2. 创建短信发送服务类
  3. 创建验证码控制器
  4. 路由配置
  5. 使用示例
  6. 安全优化建议
  7. 注意事项

在ThinkPHP项目中实现短信验证码发送,通常需要以下几个步骤:

配置短信服务商

config/目录下创建短信配置:

// config/sms.php
return [
    'default' => 'aliyun',
    'drivers' => [
        'aliyun' => [
            'access_key_id' => 'your-access-key-id',
            'access_key_secret' => 'your-access-key-secret',
            'sign_name' => '您的签名',
            'template_code' => 'SMS_0000000',
        ],
        'tencent' => [
            'secret_id' => 'your-secret-id',
            'secret_key' => 'your-secret-key',
            'sdk_app_id' => 'your-app-id',
            'sign_name' => '您的签名',
            'template_id' => 'your-template-id',
        ],
    ],
];

创建短信发送服务类

// app/service/SmsService.php
namespace app\service;
use think\Config;
use think\facade\Log;
class SmsService
{
    protected $config;
    public function __construct()
    {
        $this->config = config('sms.');
    }
    /**
     * 发送验证码
     * @param string $phone 手机号
     * @return array
     */
    public function sendCode($phone)
    {
        // 生成4位或6位验证码
        $code = $this->generateCode(6);
        // 存储验证码到缓存(5分钟有效)
        cache("sms_code_{$phone}", $code, 300);
        // 根据配置选择短信服务商
        $result = $this->sendSms($phone, $code);
        if ($result['code'] == 1) {
            return ['status' => true, 'msg' => '验证码发送成功'];
        } else {
            cache("sms_code_{$phone}", null);
            return ['status' => false, 'msg' => '验证码发送失败'];
        }
    }
    /**
     * 发送短信
     * @param string $phone
     * @param string $code
     */
    protected function sendSms($phone, $code)
    {
        try {
            // 使用阿里云短信
            return $this->sendByAliyun($phone, $code);
        } catch (\Exception $e) {
            Log::error('短信发送失败: ' . $e->getMessage());
            return ['code' => 0, 'msg' => $e->getMessage()];
        }
    }
    /**
     * 阿里云短信
     */
    protected function sendByAliyun($phone, $code)
    {
        $config = $this->config['drivers']['aliyun'];
        // 引入阿里云SDK
        // 使用 require 引入 aliyun-sdk.php
        $params = [
            'PhoneNumbers' => $phone,
            'SignName' => $config['sign_name'],
            'TemplateCode' => $config['template_code'],
            'TemplateParam' => json_encode(['code' => $code]),
        ];
        // 此处为伪代码,实际需要根据阿里云SDK进行调用
        // $result = \Aliyun\Sms\SendSms::send($config, $params);
        // 模拟发送成功
        return ['code' => 1];
    }
    /**
     * 生成验证码
     * @param int $length
     * @return string
     */
    protected function generateCode($length = 6)
    {
        $code = '';
        for ($i = 0; $i < $length; $i++) {
            $code .= rand(0, 9);
        }
        return $code;
    }
    /**
     * 验证验证码
     * @param string $phone
     * @param string $code
     * @return bool
     */
    public function verifyCode($phone, $code)
    {
        $cachedCode = cache("sms_code_{$phone}");
        if ($cachedCode === false || $cachedCode != $code) {
            return false;
        }
        // 验证成功后删除缓存
        cache("sms_code_{$phone}", null);
        return true;
    }
}

创建验证码控制器

// app/controller/Sms.php
namespace app\controller;
use think\Request;
use app\service\SmsService;
use think\facade\Validate;
class Sms
{
    protected $smsService;
    public function __construct(SmsService $smsService)
    {
        $this->smsService = $smsService;
    }
    /**
     * 发送验证码
     * @param Request $request
     */
    public function sendCode(Request $request)
    {
        // 获取手机号
        $phone = $request->post('phone');
        // 验证手机号格式
        $validate = Validate::rule([
            'phone' => 'require|mobile',
        ]);
        if (!$validate->check(['phone' => $phone])) {
            return json(['code' => 0, 'msg' => '手机号格式不正确']);
        }
        // 检查发送频率(60秒内只能发送一次)
        $lastSendTime = cache("sms_send_time_{$phone}");
        if ($lastSendTime && time() - $lastSendTime < 60) {
            return json(['code' => 0, 'msg' => '发送过于频繁,请稍后再试']);
        }
        // 发送验证码
        $result = $this->smsService->sendCode($phone);
        if ($result['status']) {
            // 记录发送时间
            cache("sms_send_time_{$phone}", time(), 300);
            return json(['code' => 1, 'msg' => $result['msg']]);
        } else {
            return json(['code' => 0, 'msg' => $result['msg']]);
        }
    }
    /**
     * 验证验证码
     * @param Request $request
     */
    public function verifyCode(Request $request)
    {
        $phone = $request->post('phone');
        $code = $request->post('code');
        if ($this->smsService->verifyCode($phone, $code)) {
            return json(['code' => 1, 'msg' => '验证成功']);
        } else {
            return json(['code' => 0, 'msg' => '验证码错误或已过期']);
        }
    }
}

路由配置

// route/app.php
use think\facade\Route;
Route::post('sms/send', 'Sms/sendCode');
Route::post('sms/verify', 'Sms/verifyCode');

使用示例

// 前端JS示例
function sendSms() {
    const phone = document.getElementById('phone').value;
    $.ajax({
        url: '/sms/send',
        method: 'POST',
        data: { phone: phone },
        success: function(response) {
            if (response.code == 1) {
                alert('验证码已发送');
                // 开始倒计时
                startCountdown();
            } else {
                alert(response.msg);
            }
        }
    });
}
function startCountdown() {
    let seconds = 60;
    const timer = setInterval(() => {
        seconds--;
        if (seconds <= 0) {
            clearInterval(timer);
            document.getElementById('sendBtn').textContent = '重新发送';
        } else {
            document.getElementById('sendBtn').textContent = seconds + '秒后重发';
        }
    }, 1000);
}

安全优化建议

class SmsService
{
    // 添加IP限制、设备限制等
    protected function checkLimit($phone, $ip)
    {
        // 使用Redis或缓存记录
        $key = "sms_limit_{$phone}";
        $count = cache($key) ?: 0;
        if ($count >= 5) {
            return false; // 当日发送次数过多
        }
        cache($key, $count + 1, 86400); // 24小时
        return true;
    }
    // 图形验证码保护
    protected function needCaptcha($phone)
    {
        // 判断是否需要图形验证码
        $failCount = cache("sms_fail_{$phone}") ?: 0;
        return $failCount >= 3;
    }
}

注意事项

  1. 定时清理:定期清理缓存中的验证码
  2. 日志记录:记录所有发送记录
  3. 错误处理:合理处理各种异常情况
  4. 性能考虑:使用缓存存储验证码,避免数据库压力
  5. 安全防护:防止短信轰炸,添加频率限制

这样可以实现一个完整、安全的短信验证码发送功能,根据实际使用的短信服务商,需要调整具体的SDK调用代码。

抱歉,评论功能暂时关闭!