註冊時常常須要用到短信驗證碼,本文記錄一下思路和具體實現。
短信驗證平臺使用雲片,短信驗證碼的生成使用thinkphp。php
一、用戶輸入手機號,請求獲取短信驗證碼。
二、thinkphp生成短信驗證碼,存儲,同時和其餘參數一塊兒發送請求給雲片。
三、雲片發送短信驗證碼到指定手機號。
四、用戶輸入短信驗證碼。
五、thinkphp根據驗證碼是否正確、驗證碼是否過時兩個條件判斷是否驗證經過。html
接口地址:https://sms.yunpian.com/v1/sms/send.json
。
使用postman,輸入三個必須的參數apikey
、mobile
和text
。
前端
使用php的curl函數發起https請求,帶入參數apikey
、mobile
和text
。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 $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:json
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); } } } } }
驗證短信驗證碼時間是否過時,驗證短信驗證碼是否正確。api
// 驗證短信驗證碼是否有效 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提供了生成圖片驗證碼的函數,下面咱們來實現驗證碼的生成、刷新和驗證。session
// 獲取圖片驗證碼,刷新圖片驗證碼 public function getPicCode(){ $config = array( 'fontSize'=>30, // 驗證碼字體大小 'length'=>4, // 驗證碼位數 'useNoise'=>false, // 關閉驗證碼雜點 'expire'=>600 ); $Verify = new \Think\Verify($config); $Verify->entry(2333);//2333是驗證碼標誌 }
假設,該函數的對應url爲http://localhost/owner-bd/index.php/Home/CheckCode/getPicCode
,那麼,圖片驗證碼的地址就是這個url,放入頁面圖片標籤的src屬性便可。
// 驗證驗證碼是否正確 public function checkPicCode($code){ $verify = new \Think\Verify(); 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 \Think\Verify(); 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
cURL函數
http://php.net/manual/zh/ref....
curl 基礎例子
http://php.net/manual/zh/curl...
在PHP語言中使用JSON
http://www.ruanyifeng.com/blo...
thinkphp驗證碼
http://document.thinkphp.cn/m...
修改ThinkPHP的驗證碼類
http://www.cnblogs.com/BTMast...
ThinkPHP 3.2版本 , 沒法讀取$_SESSION['verify_code']
http://www.cnblogs.com/lovezb...
LICEcap - Download
http://licecap.en.softonic.com/
gif動態圖局部加馬賽克模糊廣告文字
http://www.leawo.cn/space-138...