前言
注册时经常需要用到短信验证码,本文记录一下思路和具体实现。
短信验证平台使用云片,短信验证码的生成使用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。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
// 获取短信验证码 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 ; } |
生成随机短信验证码
默认生成四位的随机短信验证码。
1
2
3
4
5
6
|
// 生成短信验证码 public function createsmscode( $length = 4){ $min = pow(10 , ( $length - 1)); $max = pow(10, $length ) - 1; return rand( $min , $max ); } |
整合
在数据库新建表sun_smscode:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
|
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); } } } } } |
验证短信验证码
验证短信验证码时间是否过期,验证短信验证码是否正确。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
// 验证短信验证码是否有效 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提供了生成图片验证码的函数,下面我们来实现验证码的生成、刷新和验证。
生成和刷新图片验证码
1
2
3
4
5
6
7
8
9
10
11
|
// 获取图片验证码,刷新图片验证码 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属性即可。
验证图片验证码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
// 验证验证码是否正确 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方法。
跑通前后端
后端修改
验证图片验证码函数,改为被调用函数:
1
2
3
4
5
6
7
8
|
public function checkpiccode( $piccode ){ $verify = new \think\verify(); if ( $verify ->check( $piccode , 2333)){ return true; } else { return false; }; } |
在获取短信验证码函数的最顶部,添加调用图片验证码函数,只有通过验证,才发送请求给云片。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
// 获取短信验证码 public function getsmscode(){ $piccode = $_post [ 'piccode' ]; if (! $this ->checkpiccode( $piccode )){ $result = array ( 'code' => '1' , 'ext' => '验证码错误,请重新输入' ); echo json_encode( $result ,json_unescaped_unicode); return ; } /*省略*/ } |
前端核心代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
|
<!--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值来标记。
1
2
|
$_session [ 'checkpiccode' ] = true; $_session [ 'checksmscode' ] = true; |
在最后一步,向数据库中添加用户时,先验证一下两个session值是否都为true,都为true时再添加。
成果
后记
以后也许有用的代码:
1
2
|
echo json_encode( $_session ); // 打印出session中的数据 echo session_id(); // 打印当前session的id |
以上所述是小编给大家介绍的thinkphp实现短信验证注册,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对服务器之家网站的支持!
原文链接:http://www.cnblogs.com/voidking/p/sms-verification-code.html