머리말
등록 시 SMS 인증 코드가 자주 사용됩니다. 이 글에는 아이디어와 구체적인 구현 내용이 기록되어 있습니다.
SMS 인증 플랫폼은 Yunpian을 사용하고, SMS 인증 코드 생성은 thinkphp를 사용합니다.
생각
1. 사용자가 휴대폰 번호를 입력하고 SMS 인증 코드를 요청합니다.
2. Thinkphp는 SMS 인증 코드를 생성하여 저장한 후 다른 매개변수와 함께 Yunpian에 요청을 보냅니다.
3. 윤피안은 지정된 휴대폰 번호로 문자 메시지 인증 코드를 보냅니다.
4. 사용자가 SMS 인증번호를 입력합니다.
5. thinkphp는 인증 코드가 올바른지, 인증 코드가 만료되었는지 여부라는 두 가지 조건에 따라 인증 통과 여부를 결정합니다.
코드 구현
인증 인터페이스
인터페이스 주소: https://sms.yunpian.com/v1/sms/send.json.
Postman을 사용하여 apikey, mobile, text 세 가지 필수 매개변수를 입력하세요.
php가 http/https 요청을 시작합니다
PHP의 컬 기능을 사용하여 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; }
무작위 SMS 인증코드 생성
기본적으로 4자리 무작위 SMS 인증코드가 생성됩니다.
// 生成短信验证码 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); } } } } }
인증문자 인증코드
SMS 인증코드 시간이 만료되었는지, SMS 인증코드가 맞는지 확인하세요.
// 验证短信验证码是否有效 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; } }
개선 사항
SMS 폭격을 방지하기 위해 SMS 인증코드 요청 시 이미지 인증코드를 반드시 포함시켜야 합니다.
thinkphp는 이미지 인증코드 생성 기능을 제공합니다. 인증코드 생성, 새로고침, 인증을 구현해 보겠습니다.
이미지 인증코드 생성 및 새로고침
// 获取图片验证码,刷新图片验证码 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의 확인 메소드를 다시 작성할 수 있습니다.
프런트엔드와 백엔드를 통해 실행
백엔드 수정
이미지 인증코드 기능을 확인하고 호출된 기능으로 변경합니다:
public function checkPicCode($picCode){ $verify = new \Think\Verify(); if($verify->check($picCode, 2333)){ return true; }else{ return false; }; }
SMS 인증번호 받기 기능 상단에 이미지 인증번호를 호출하는 기능을 추가하면 인증을 통과한 후에만 Yunpian으로 요청이 전송됩니다.
// 获取短信验证码 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['checkPicCode'] = true; $_SESSION['checkSMSCode'] = true;
마지막 단계에서 데이터베이스에 사용자를 추가할 때 먼저 두 세션 값이 모두 true인지 확인한 후 둘 다 true인 경우 추가합니다.
결과
후기
미래에 유용할 코드:
echo json_encode($_SESSION);// 打印出session中的数据 echo session_id();// 打印当前session的id
위는 편집자가 소개한 Thinkphp의 SMS 확인 등록 구현입니다. 궁금한 사항이 있으면 메시지를 남겨주시면 편집자가 시간에 맞춰 답변해 드리겠습니다. 또한 Script House 웹사이트를 지원해 주시는 모든 분들께 감사의 말씀을 전하고 싶습니다!