Thinkphp实现短信验证注册功能
author:一佰互联 2019-04-26   click:166

前言

注册时经常需要用到短信验证码,本文记录一下思路和具体实现。

短信验证平台使用云片,短信验证码的生成使用thinkphp。

思路

1、用户输入手机号,请求获取短信验证码。

2、thinkphp生成短信验证码,存储,同时和其他参数一起发送请求给云片。

3、云片发送短信验证码到指定手机号。

4、用户输入短信验证码。

5、thinkphp根据验证码是否正确、验证码是否过期两个条件判断是否验证通过。

代码实现

验证接口

接口地址:https://sms.yunpian.com/v1/sms/send.json。

使用postman,输入三个必须的参数apikey、mobile和text。

php发起http/https请求

使用php的curl函数发起https请求,带入参数apikey、mobile和text。

// 获取短信验证码
public function getSMSCode(){
// create curl resource 
$ch = curl_init(); 
// set url
$url = "https://sms.yunpian.com/v1/sms/send.json"; 
curl_setopt($ch, CURLOPT_URL, $url); 
// set param
$paramArr = array(
"apikey" => "******",
"mobile" => "******",
"text" => "【小太阳】您的验证码是1234"
);
$param = "";
foreach ($paramArr as $key => $value) {
$param .= urlencode($key)."=".urlencode($value)."&";
}
$param = substr($param, 0, strlen($param)-1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $param);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_POST, 1);
//curl默认不支持https协议,设置不验证协议
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 
//return the transfer as a string 
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 
// $output contains the output string 
$output = curl_exec($ch); 
// close curl resource to free up system resources 
curl_close($ch); 
echo $output;
}

生成随机短信验证码

默认生成四位的随机短信验证码。

// 生成短信验证码
public function createSMSCode($length = 4){
$min = pow(10 , ($length - 1));
$max = pow(10, $length) - 1;
return rand($min, $max);
}

整合

在数据库新建表sun_smscode:

DROP TABLE IF EXISTS `sun_smscode`;
CREATE TABLE `sun_smscode` (
`id` int(8) NOT NULL AUTO_INCREMENT,
`mobile` varchar(11) NOT NULL,
`code` int(4) NOT NULL,
`create_at` datetime NOT NULL,
`update_at` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
thinkphp代码:
// 获取短信验证码
public function getSMSCode(){
// create curl resource 
$ch = curl_init(); 
// set url
$url = "https://sms.yunpian.com/v1/sms/send.json"; 
curl_setopt($ch, CURLOPT_URL, $url); 
// set param
$mobile = $_POST["mobile"];
$code = $this->createSMSCode();
$paramArr = array(
"apikey" => "******",
"mobile" => $mobile,
"text" => "【小太阳】您的验证码是".$code
);
$param = "";
foreach ($paramArr as $key => $value) {
$param .= urlencode($key)."=".urlencode($value)."&";
}
$param = substr($param, 0, strlen($param)-1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $param);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //不验证证书下同
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 
//return the transfer as a string 
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 
// $output contains the output string 
$output = curl_exec($ch); 
// close curl resource to free up system resources 
curl_close($ch); 
//$outputJson = json_decode($output);
$outputArr = json_decode($output, true);
//echo $outputJson->code;
//echo $outputArr["code"];
if($outputArr["code"] == "0"){
$data["mobile"] = $mobile;
$data["code"] = $code;
$smscode = D("smscode");
$smscodeObj = $smscode->where("mobile="$mobile"")->find();
if($smscodeObj){
$data["update_at"] = date("Y-m-d H:i:s");
$success = $smscode->where("mobile="$mobile"")->save($data);
if($success !== false){
$result = array(
"code" => "0",
"ext" => "修改成功",
"obj" => $smscodeObj
);
}
echo json_encode($result,JSON_UNESCAPED_UNICODE);
}else{
$data["create_at"] = date("Y-m-d H:i:s");
$data["update_at"] = $data["create_at"];
if($smscode->create($data)){
$id = $smscode->add();
if($id){
$smscode_temp = $smscode->where("id="$id"")->find();
$result = array(
"code"=> "0",
"ext"=> "创建成功",
"obj"=>$smscode_temp
);
echo json_encode($result,JSON_UNESCAPED_UNICODE);
}
}
}
}
}

验证短信验证码

验证短信验证码时间是否过期,验证短信验证码是否正确。

// 验证短信验证码是否有效
public function checkSMSCode(){
$mobile = $_POST["mobile"];
$code = $_POST["code"];
$nowTimeStr = date("Y-m-d H:i:s");
$smscode = D("smscode");
$smscodeObj = $smscode->where("mobile="$mobile"")->find();
if($smscodeObj){
$smsCodeTimeStr = $smscodeObj["update_at"];
$recordCode = $smscodeObj["code"];
$flag = $this->checkTime($nowTimeStr, $smsCodeTimeStr);
if(!$flag){
$result = array(
"code" => "1",
"ext" => "验证码过期,请刷新后重新获取"
);
echo json_encode($result,JSON_UNESCAPED_UNICODE);
return;
}
if($code != $recordCode){
$result = array(
"code" => "2",
"ext" => "验证码错误,请重新输入"
);
echo json_encode($result,JSON_UNESCAPED_UNICODE);
return;
}
$result = array(
"code" => "0",
"ext" => "验证通过"
);
echo json_encode($result,JSON_UNESCAPED_UNICODE);
}
}
// 验证验证码时间是否过期
public function checkTime($nowTimeStr,$smsCodeTimeStr){
//$nowTimeStr = "2016-10-15 14:39:59";
//$smsCodeTimeStr = "2016-10-15 14:30:00";
$nowTime = strtotime($nowTimeStr);
$smsCodeTime = strtotime($smsCodeTimeStr);
$period = floor(($nowTime-$smsCodeTime)/60); //60s
if($period>=0 && $period<=20){
return true;
}else{
return false;
}
}

改进

为了防止短信轰炸,在请求获取短信验证码时,需要加入图片验证码。

thinkphp提供了生成图片验证码的函数,下面我们来实现验证码的生成、刷新和验证。

生成和刷新图片验证码

// 获取图片验证码,刷新图片验证码
public function getPicCode(){
$config = array(
"fontSize"=>30, // 验证码字体大小
"length"=>4, // 验证码位数
"useNoise"=>false, // 关闭验证码杂点
"expire"=>600
);
$Verify = new ThinkVerify($config);
$Verify->entry(2333);//2333是验证码标志
}

假设,该函数的对应url为http://localhost/owner-bd/index.php/Home/CheckCode/getPicCode,那么,图片验证码的地址就是这个url,放入页面图片标签的src属性即可。

验证图片验证码

// 验证验证码是否正确
public function checkPicCode($code){
$verify = new ThinkVerify();
if($verify->check($code, 2333)){
$result = array(
"code" => "0",
"ext" => "验证通过"
);
echo json_encode($result,JSON_UNESCAPED_UNICODE);
}else{
$result = array(
"code" => "1",
"ext" => "验证码错误,请重新输入"
);
echo json_encode($result,JSON_UNESCAPED_UNICODE);
};
}

以上方法,我们利用了thinkphp提供的check方法,实现起来很简单。但是,如果想要得到验证细节,就没有办法了。比如,验证码错误,可能验证码超时,可能因为输入验证码错误,可能因为验证码已经使用过等等。必要的时候,可以重写thinkphp的验证码类,或者重写thinkphp的check方法。

跑通前后端

后端修改

验证图片验证码函数,改为被调用函数:

public function checkPicCode($picCode){
$verify = new ThinkVerify();
if($verify->check($picCode, 2333)){
return true;
}else{
return false;
};
}

在获取短信验证码函数的最顶部,添加调用图片验证码函数,只有通过验证,才发送请求给云片。

// 获取短信验证码
public function getSMSCode(){
$picCode = $_POST["picCode"];
if(!$this->checkPicCode($picCode)){
$result = array(
"code" => "1",
"ext" => "验证码错误,请重新输入"
);
echo json_encode($result,JSON_UNESCAPED_UNICODE);
return;
}
/*省略*/
}

前端核心代码

<!--register.html-->
<!DOCTYPE html>
<html lang="zh" ng-app="sunApp">
<head>
<meta charset="UTF-8">
<title>注册</title>
</head>
<body ng-controller="registerController">
<form action="" class="register-form" ng-show="isShow1">
<div class="input-group">
<input type="text" class="mobile" ng-model="mobile" placeholder="手机号">
</div>
<div class="input-group">
<input type="text" class="pic-code" ng-model="picCode" placeholder="图片验证码">
<img class="img" src="{{picCodeUrl}}" alt="" ng-click="refresh()">
</div>
<div class="input-group">
<input type="text" class="sms-code" ng-model="SMSCode" placeholder="短信验证码">
<button class="btn-sms" ng-click="getSMSCode()" ng-disabled="btnSMSDisabled">{{btnSMSText}}</button>
</div>
<button class="confirm-btn" ng-click="next()">下一步</button>
</form>
<form action="" class="register-form" ng-show="isShow2">
<div class="input-group">
<input type="text" class="mobile" ng-model="mobile" placeholder="手机号" disabled="true">
</div>
<div class="input-group">
<input type="password" class="password" ng-model="password" placeholder="请输入密码">
<input type="password" class="password" ng-model="password2" placeholder="请再次输入密码">
</div>
<button class="confirm-btn" ng-click="getSMSCode()">注册</button>
</form>
</body>
</html>
// register.js
angular.module("sunApp").controller("registerController", function ($scope,$http,$httpParamSerializer,$state,$interval) { 
$scope.picCodeUrl = "/owner-bd/index.php/Home/CheckCode/getPicCode";
$scope.isShow1 = true;
$scope.isShow2 = false;
$scope.btnSMSText = "获取验证码";
$scope.btnSMSDisabled = false;
$scope.checkOver = false;
// 获取短信验证码
$scope.getSMSCode = function(){
var param = {
mobile: $scope.mobile,
picCode: $scope.picCode
};
$http({
method:"POST",
url:"/owner-bd/index.php/Home/SMS/getSMSCode",
//url: "/owner-fd/mock/common.json",
headers:{
"Content-Type":"application/x-www-form-urlencoded"
},
dataType: "json",
data: $httpParamSerializer(param)
}).then(function successCallback(response) {
console.log(response.data);
if(response.data.code == "0"){
$scope.checkOver = true;
$scope.btnSMSDisabled = true;
var time = 60;
var timer = null;
timer = $interval(function(){
time = time - 1;
$scope.btnSMSText = time+"秒";
if(time == 0) {
$interval.cancel(timer);
$scope.btnSMSDisabled = false;
$scope.btnSMSText = "重新获取";
}
}, 1000);
}
}, function errorCallback(response) {
console.log(response.data);
});
}
// 验证短信验证码
$scope.next = function(){
if(!$scope.checkOver){
console.log("未通过验证");
return;
}
var param = {
mobile: $scope.mobile,
code: $scope.SMSCode
};
$http({
method:"POST",
url:"/owner-bd/index.php/Home/SMS/checkSMSCode",
//url: "/owner-fd/mock/common.json",
headers:{
"Content-Type":"application/x-www-form-urlencoded"
},
dataType: "json",
data: $httpParamSerializer(param)
}).then(function successCallback(response) {
console.log(response.data);
if(response.data.code == "0"){
$scope.isShow1 = false;
$scope.isShow2 = true;
}
}, function errorCallback(response) {
console.log(response.data);
});
}
// 刷新图片验证码
$scope.refresh = function(){
$scope.picCodeUrl = "/owner-bd/index.php/Home/CheckCode/getPicCode?"+Math.random();
}
});

优化

以上代码,安全性不是很好,我们可以利用工具绕过前端验证。为了避免这个问题,可以在checkPicCode和checkSMSCode函数中添加session值来标记。

$_SESSION["checkPicCode"] = true;
$_SESSION["checkSMSCode"] = true;

在最后一步,向数据库中添加用户时,先验证一下两个session值是否都为true,都为true时再添加。

成果

后记

以后也许有用的代码:

echo json_encode($_SESSION);// 打印出session中的数据
echo session_id();// 打印当前session的id

以上所述是小编给大家介绍的Thinkphp实现短信验证注册,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网页设计网站的支持!