- 前端:用户在网页上填写表单,并触发短信发送和验证逻辑。
- 后端:DedeCMS处理表单提交,并调用短信接口发送验证码。
- 数据库:存储验证码和其状态(是否已使用、过期时间等)。
下面我将为你提供一个完整、详细的实现方案,包含代码示例和步骤说明。
核心思路
- 分离验证码逻辑:在用户点击“获取验证码”按钮时,通过AJAX请求后端接口,后端生成验证码、调用短信接口发送,并将验证码和相关信息存入数据库。
- 二次验证:在用户最终提交整个表单时,后端再次验证用户输入的验证码是否与数据库中存储的、且未过期的验证码匹配。
- 使用DedeCMS的自定义表单功能:我们将在DedeCMS原有的表单处理流程上,增加验证码的校验环节。
第一步:准备工作
- 短信平台账号:你需要先注册一个短信服务提供商(如:阿里云短信、腾讯云短信、SendCloud等),获取你的
API ID、API Key和短信签名。 - DedeCMS环境:确保你的网站已经安装并可以正常运行DedeCMS。
- 创建自定义表单:
- 进入DedeCMS后台 -> 核心 -> 内容模型管理 -> 自定义表单。
- 创建一个新的自定义表单,例如命名为
feedback。 - 在表单字段管理中,添加以下字段:
tel(手机号码) -varchar(20)- 必填code(短信验证码) -varchar(10)- 必填code_time(验证码时间戳) -int(11)- 用于判断是否过期
- 保存并生成表单,你会得到类似
plus/diy.php?action=list&diyid=1的列表页和表单提交页 (plus/diy.php)。
第二步:创建短信发送接口(后端核心)
这是最关键的一步,我们需要一个PHP文件来专门处理验证码的生成、发送和存储。
-
在你的网站根目录下创建一个新文件,
/plus/send_sms.php。 -
修改
/plus/send_sms.php文件内容:
<?php
require_once(dirname(__FILE__)."/../include/common.inc.php");
// 引入短信平台SDK(这里以阿里云短信为例,你需要根据你的短信平台修改)
// 假设你已经将阿里云的SDK文件放在 /include/aliyun-php-sdk/ 目录下
// include_once 'aliyun-php-sdk-core/Config.php';
// use Dysmsapi\V20250525\Dysmsapi;
// use DefaultProfile;
// use DefaultAcsClient;
// --- 配置信息 ---
$smsConfig = array(
'accessKeyId' => '你的AccessKeyId', // 阿里云AccessKeyId
'accessKeySecret' => '你的AccessKeySecret', // 阿里云AccessKeySecret
'signName' => '你的短信签名', // 短信签名
'templateCode' => 'SMS_123456789', // 短信模板ID (模板内容需包含变量: ${code})
'expireMinutes' => 5, // 验证码有效期(分钟)
);
// --- 获取前端传来的参数 ---
$mobile = isset($mobile) ? trim($mobile) : '';
if(empty($mobile) || !preg_match("/^1[3-9]\d{9}$/", $mobile)) {
// 返回JSON错误信息
exit(json_encode(array('status' => -1, 'msg' => '手机号码格式错误')));
}
// --- 生成随机验证码 ---
$code = rand(100000, 999999);
// --- 调用短信接口发送(伪代码,需替换为实际SDK调用) ---
try {
// 1. 初始化AcsClient
// $profile = DefaultProfile::getProfile("cn-hangzhou", $smsConfig['accessKeyId'], $smsConfig['accessKeySecret']);
// $acclient = new DefaultAcsClient($profile);
// $request = new Dysmsapi\SendSmsRequest();
// $request->setPhoneNumbers($mobile);
// $request->setSignName($smsConfig['signName']);
// $request->setTemplateCode($smsConfig['templateCode']);
// $request->setTemplateParam(json_encode(array('code' => $code)));
// 2. 发送短信
// $response = $acclient->getAcsResponse($request);
// 为了演示,我们假设发送总是成功
// $sendResult = json_decode(json_encode($response), true);
// if($sendResult['Code'] != 'OK') {
// exit(json_encode(array('status' => -2, 'msg' => '短信发送失败: ' . $sendResult['Message'])));
// }
// --- 3. 将验证码存入数据库 ---
$tableName = '#@__diyform'; // DedeCMS自定义表单的数据表前缀
$dsql = new DedeSql(false);
// 先删除该手机号旧的验证码记录
$dsql->ExecuteNoneQuery("DELETE FROM `{$tableName}` WHERE `tel` = '{$mobile}' AND `type` = 'sms_verify'");
// 插入新的验证码记录
$now_time = time();
$expire_time = $now_time + ($smsConfig['expireMinutes'] * 60);
$sql = "INSERT INTO `{$tableName}` (`tel`, `code`, `code_time`, `type`, `addtime`)
VALUES ('{$mobile}', '{$code}', '{$expire_time}', 'sms_verify', '{$now_time}');";
$dsql->ExecuteNoneQuery($sql);
// --- 返回成功信息 ---
exit(json_encode(array('status' => 1, 'msg' => '验证码已发送,请注意查收')));
} catch (Exception $e) {
exit(json_encode(array('status' => -3, 'msg' => '系统错误: ' . $e->getMessage())));
}
?>
代码说明:
- 短信平台SDK:上面的代码中,阿里云短信的调用部分被注释了并标为“伪代码”,你需要去你选择的短信平台下载对应的PHP SDK,并根据其文档修改这部分代码。
- 数据库表:我们直接利用了DedeCMS存储自定义表单的数据表
#@__diyform,为了不干扰正常表单数据,我们增加了一个type字段,将其值设为'sms_verify'来区分。code_time存储的是过期时间戳,方便后续判断。 - 安全性:这里为了简化,没有做太复杂的防刷限制(如频率限制),在生产环境中,你应该增加对同一手机号发送频率的限制(60秒内不能重复发送)。
第三步:修改前端表单页面
我们需要修改用户填写表单的页面(通常是 /plus/diy.php 或你生成的静态表单页面),增加获取验证码和AJAX提交的逻辑。
假设你的表单HTML结构如下(从DedeCMS生成的表单中获取):
<form action="/plus/diy.php" enctype="multipart/form-data" method="post">
<input type="hidden" name="action" value="post" />
<input type="hidden" name="diyid" value="1" />
<input type="hidden" name="do" value="2" />
<div>
<label for="tel">手机号码:</label>
<input type="text" name="tel" id="tel" required />
</div>
<div>
<label for="code">短信验证码:</label>
<input type="text" name="code" id="code" required />
<button type="button" id="getSmsBtn">获取验证码</button>
</div>
<!-- 其他表单字段... -->
<button type="submit">提交</button>
</form>
修改步骤:
-
引入jQuery库:确保你的页面有jQuery,如果没有,在
<head>中引入:<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
-
编写JavaScript代码:在页面底部
</body>标签前,添加以下脚本:
<script>
$(function() {
var getSmsBtn = $('#getSmsBtn');
var telInput = $('#tel');
var countdown = 60;
// 点击“获取验证码”按钮
getSmsBtn.on('click', function() {
var mobile = telInput.val().trim();
if (!mobile) {
alert('请先输入手机号码');
return;
}
// 禁用按钮,开始倒计时
getSmsBtn.prop('disabled', true);
var timer = setInterval(function() {
countdown--;
getSmsBtn.text(countdown + '秒后重试');
if (countdown <= 0) {
clearInterval(timer);
getSmsBtn.prop('disabled', false).text('获取验证码');
countdown = 60;
}
}, 1000);
// 发送AJAX请求到我们创建的接口
$.ajax({
url: '/plus/send_sms.php',
type: 'POST',
data: { mobile: mobile },
dataType: 'json',
success: function(response) {
if (response.status == 1) {
alert(response.msg);
} else {
alert(response.msg);
// 如果发送失败,恢复按钮
clearInterval(timer);
getSmsBtn.prop('disabled', false).text('获取验证码');
countdown = 60;
}
},
error: function() {
alert('网络错误,请稍后重试');
clearInterval(timer);
getSmsBtn.prop('disabled', false).text('获取验证码');
countdown = 60;
}
});
});
// 原有的表单提交逻辑可以保留,也可以改为AJAX提交
// 如果是AJAX提交,你需要阻止默认的form提交行为,然后用$.ajax提交整个表单数据
// 这里为了简单,我们假设还是用传统的form提交,验证码的校验将在下一步的后端PHP中完成
});
</script>
第四步:修改DedeCMS表单处理逻辑(后端校验)
也是最关键的一步,我们需要修改DedeCMS处理表单提交的文件,在数据入库前,先校验验证码。
-
找到并备份文件:
/plus/diy.php。务必先备份! -
修改
/plus/diy.php: 我们需要找到处理do=2(即表单提交)的代码段,在if($dsql->ExecuteNoneQuery($query))之前,插入我们的验证码校验逻辑。在
diy.php中找到类似这样的代码块(大约在第150-200行,具体版本可能略有不同):// ... 前面的代码 ... if($do == 2) { $dede_fields = empty($dede_fields) ? '' : trim($dede_fields); $dede_fieldshash = empty($dede_fieldshash) ? '' : trim($dede_fieldshash); if($dede_fields && $dede_fieldshash) { $fdhash = md5($dede_fields.$cfg_cookie_encode); if($fdhash != $dede_fieldshash) { ShowMsg('数据校验不对,程序返回', '-1'); exit(); } } $fieldarr = explode(';', $dede_fields); if(is_array($fieldarr)) { foreach($fieldarr as $field) { if($field == '') continue; $fieldinfo = explode(',', $field); ${$fieldinfo[0]} = FilterSearch(${$fieldinfo[0]}); ${$fieldinfo[0]} = ${$fieldinfo[0]} ? ${$fieldinfo[0]} : ${$fieldinfo[2]}; if($fieldinfo[1] == 'textdata') { ${$fieldinfo[0]} = HtmlReplace(${$fieldinfo[0]}, 1); } } } // 在这里插入我们的验证码校验代码! $addvar = ''; $inadd_f = ''; $inadd_v = ''; if(is_array($fieldarr)) { foreach($fieldarr as $field) { if($field == '') continue; $fieldinfo = explode(',', $field); if(${$fieldinfo[0]} != '' && $fieldinfo[1] != 'textdata') { $inadd_f .= ','.$fieldinfo[0]; $inadd_v .= " ,'".${$fieldinfo[0]}."'"; } } } $query = "INSERT INTO `{$diy->table}` (`id`, `ifcheck` $inadd_f ) VALUES (NULL, $ifcheck $inadd_v );"; if($dsql->ExecuteNoneQuery($query)) { // ... 后面的代码 ... -
在指定位置插入校验代码:
在上面代码注释
// 在这里插入我们的验证码校验代码!的位置,粘贴以下PHP代码:// --- 开始:短信验证码校验逻辑 --- $mobile = isset($tel) ? $tel : ''; $user_code = isset($code) ? $code : ''; if (empty($mobile) || empty($user_code)) { ShowMsg('手机号和验证码不能为空!', '-1'); exit(); } $dsql->SetQuery("SELECT `code`, `code_time` FROM `{$diy->table}` WHERE `tel` = '{$mobile}' AND `type` = 'sms_verify' ORDER BY `id` DESC LIMIT 1"); $dsql->Execute(); if($row = $dsql->GetArray()) { $db_code = $row['code']; $code_time = $row['code_time']; $current_time = time(); // 验证码是否过期 if ($current_time > $code_time) { ShowMsg('验证码已过期,请重新获取!', '-1'); exit(); } // 验证码是否正确 if ($db_code != $user_code) { ShowMsg('验证码错误,请重新输入!', '-1'); exit(); } // 验证成功,删除已使用的验证码记录(防止重复使用) $dsql->ExecuteNoneQuery("DELETE FROM `{$diy->table}` WHERE `tel` = '{$mobile}' AND `type` = 'sms_verify'"); } else { ShowMsg('未找到有效的验证码记录,请先获取验证码!', '-1'); exit(); } // --- 结束:短信验证码校验逻辑 --- -
保存文件。
总结与流程回顾
整个流程已经打通:
- 用户操作:用户输入手机号,点击“获取验证码”。
- AJAX请求:前端JS向
/plus/send_sms.php发送请求。 - 发送短信:
send_sms.php生成验证码,调用短信平台API发送,并将验证码和过期时间存入#@__diyform表,标记type='sms_verify'。 - 用户输入:用户收到短信,将验证码填入表单。
- 表单提交:用户点击提交,表单数据POST到
/plus/diy.php。 - 后端校验:
diy.php在处理数据前,先从数据库中查询该手机号对应的最新验证码记录,检查用户输入的验证码是否匹配且未过期。 - 成功/失败:
- 成功:校验通过,删除数据库中的验证码记录,然后继续执行原有的DedeCMS表单入库逻辑。
- 失败:校验不通过(错误/过期),使用
ShowMsg返回错误提示,并终止后续流程。
重要提示:
- 短信费用:发送短信会产生费用,请合理控制发送频率。
- 安全性:此方案为基本实现,对于高安全要求的场景,还需要考虑更多的安全措施,如IP限制、验证码复杂度等。
- 数据库表:直接操作
#@__diyform表是一种“取巧”的方法,为了更规范,你也可以创建一个专门的验证码表来存储,这样逻辑会更清晰,也不会影响自定义表单数据的查询。
