本文目录导读:

在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;
}
}
注意事项
- 定时清理:定期清理缓存中的验证码
- 日志记录:记录所有发送记录
- 错误处理:合理处理各种异常情况
- 性能考虑:使用缓存存储验证码,避免数据库压力
- 安全防护:防止短信轰炸,添加频率限制
这样可以实现一个完整、安全的短信验证码发送功能,根据实际使用的短信服务商,需要调整具体的SDK调用代码。