短信验证码是通过发送验证码到手机的一种有效的验证码系统,作为比较准确和安全地保证购物的安全性,验证用户的正确性的一种手段,几乎网站登录都会使用该方式。
其特点是依据某些验证码接入商提供手机短信验证码服务,各网站通过接口发送请求到接入商的服务器,服务器发送随机数字或字母到手机中,由接入商的服务器统一做验证码的验证。
项目分析
- 开通阿里云、腾讯云等短信API端口;
- 为防止机器批量发送验证码,设置机器识别码,设置5分钟内发送的次数,进而予以限流;
- 发送信息错误时,进行明确的信息提示;
- 完成信息验证,执行业务正常逻辑;
核心代码
1.外部js库调用
<script type="text/javascript" src="static/js/jquery.min.js"></script> <!--layui封装库--> <script src="static/layui/layui.js" charset="utf-8"></script> <link rel="stylesheet" href="static/layui/css/layui.css">
2.HTML容器构建
<div style="margin-top: 10px;padding: 20px;"> <form class="layui-form layui-form-pane"> <div class="layui-form-item"> <label for="L_user_phone" class="layui-form-label">手机号码</label> <div class="layui-input-inline"><input type="text" id="L_user_phone" name="user_phone" class="layui-input" lay-verify="user_phone"></div> <div class="layui-form-mid layui-word-aux"><span class="x-red">* 手机号码,作为登录账户唯一凭证,如:13500000001。</span></div> </div> <div class="layui-form-item"> <label class="layui-form-label">识别码</label> <div class="layui-input-inline"><input type="text" name="captcha" id="captcha" lay-verify="required" autocomplete="off" class="layui-input"></div> <div class="layui-form-mid layui-word-aux"> <img src="?m=Login&a=loginDeal&act=getCode" id="getCode" alt="" title="点击刷新验证码" style="cursor: pointer;"><span class="x-red"> * </span> 发送手机短信验证</div> </div> <div class="layui-form-item"> <label class="layui-form-label">短信验证码</label> <div class="layui-input-inline"><input type="text" name="smscode" id="smscode" lay-verify="smscode" autocomplete="off" class="layui-input" disabled="disabled"></div> <div class="layui-input-inline"> <input type="button" class="layui-btn layui-btn-primary" id="btnSendCode" disabled="disabled" value="获取验证码"> </div> </div> <div class="layui-form-item"> <button class="layui-btn layui-btn-fluid layui-btn-normal" lay-filter="save" id="L_add" lay-submit=""> 确认登录 </button> </div> </form> </div>
3.javaScript业务逻辑验证
- 验证手机号和短信验证码规则
//自定义验证规则 form.verify({ user_phone: [/^1[3|4|5|6|7|8|9]\d{9}$/, '手机必须11位,只能是数字!'] , smscode: [/[\S]+/, "验证码为6位数且5分钟内有效"] });
- 机器识别码验证
获取实际图片识别码
$("#getCode").click(function () { $(this).attr("src", '?m=Login&a=loginDeal&act=getCode&' + Math.random()); });
- 发送短信验证码倒计时60秒
var wait = 60; function time(o) { if (wait == 0) { o.removeAttribute("disabled"); o.value = "获取验证码"; wait = 60; } else { o.setAttribute("disabled", true); o.value = "重新发送(" + wait + ")"; wait--; setTimeout(function () { time(o); }, 1000); } }
- 获取识别码和验证码的业务逻辑
//单击发送验证码; document.getElementById("btnSendCode").onclick = function () { var captcha = $("#captcha").val(); //获取识别码; $.getJSON("?m=Login&a=loginDeal&act=captcha", {mobile: mobile, captcha: captcha}, function (res) { if (res.code == '0') { layer.msg(res.msg, {icon: 1, time: 2000}); } else { if (wait != 60) { layer.alert("请" + wait + "秒后再试!", {icon: 3, time: 2000}) return; } time(document.getElementById("btnSendCode")); $.getJSON("?m=Sms&a=smsDeal&act=verify", { mobile: mobile, captcha: captcha }, function (res2) { //console.log(res2.send.Code); if (res2.send.Code != "OK") { layer.alert("短信限流,请等待5分钟", {icon: 3, time: 2000}) } }); } }); }
针对不同平台的业务逻辑,对返回的短信参数进行输出,提示用户操作。
if (res2.send.Code != "OK") { layer.alert("短信限流,请等待5分钟", {icon: 3, time: 2000}) }
- 执行业务正常逻辑
//监听提交; form.on('submit(save)', function () { $.ajax({ type: "post", url: "?m=Login&a=loginDeal&act=smsCode", async: true, data: { user_phone: $('#L_user_phone').val(), smscode: $('#smscode').val() }, dataType: "json", success: function (res) { //console.log(res); if (res.code == 0) { layer.msg(res.msg, {icon: 1, time: 2000}); } else { layer.msg(res.msg, {icon: 1, time: 2000}, function () { var index = parent.layer.getFrameIndex(window.name); parent.layer.close(index); window.parent.location.href = "?m=Index&a=my"; }); } }, error: function (err) { console.log(err); } }); return false; });
4.后端验证逻辑
- 生成随机机器识别码
case "getCode"; require_once "libs/vcode.class.php"; $obj = new vcode();//实例化; $_SESSION['authcode'] = $obj->authcode; die($obj->output()); break;
- 验证机器识别码
//网站识别码; case "captcha"; $user_phone = get_param('mobile'); $captcha = isset($_GET["captcha"]) ? trim($_GET["captcha"]) : ''; if ($captcha != $_SESSION['authcode']) { $res['code'] = "0"; $res['msg'] = "识别码错误"; die(json_encode_lockdata($res)); } else { $res['code'] = "1"; $res['msg'] = "识别码通过"; //验证码自动销毁; session_destroy(); die(json_encode_lockdata($res)); } break;
- 发送短信流(次数)判断
case "verif case "verify"; require "libs/api_demo/SmsDemo.php";/*阿里云短信SDK*/ $rmdCode = rand(111111, 999999);//随机验证码 $templateCode = 'SMS_165690909';//调用模版 $signName = '言医';//签名 $mobile = get_param('mobile');//手机号 if ($mobile == "") { die("越权访问!"); } //增加记录; addlogs($mobile, 'smscode', '', time(), getip()); //读取数据库记录; $fromTime = time() - 300; $toTime = time(); $sql = "select log_id FROM " . $db->table('log') . " WHERE 1"; $sql .= " AND logs = 'smscode' AND log_ip ='" . getip() . "'"; $sql .= " AND log_time > " . $fromTime . " AND log_time <" . $toTime . ""; $sql .= " ORDER BY log_id DESC"; $row = $db->queryall($sql); if (count($row) >= 5) { $res['code'] = 0; $res['msg'] = '5分钟内最多发送5次短信.'; $res['total'] = count($row); die(json_encode_lockdata($res)); } else { $send = SmsDemo::sendSms($mobile, $signName, $templateCode, $rmdCode, '', '', '', '', 1); $res['code'] = 1; $res['send'] = $send; $_SESSION['code'] = $rmdCode; die(json_encode_lockdata($res)); } break;
总结
一个简单的手机短信验证码登录,涉及到的方方面面比较多。在发送短信次数的限制上,本系统作为正常的业务逻辑进行判断和限流,同时对于阿里云和腾讯云也有对应的限制规则,需要在平台上进行设置。
@漏刻有时