一直没机会对接阿里的短信验证,今天自己写一个玩玩,把总结的一些点分享给大家。
代码亲测好用,直接copy走稍加改动即可。
前提:
(1)准备好阿里云短信的签名、模板、accessKeyId、accessKeySecret。签名模板没过审也可以,可以用阿里云的测试签名和模板。在弄代码之前 最好先在阿里的api调试里面先测试一下自己的签名和模板能不能成功的发送出去,具体参考阿里云 OpenAPI 开发者门户https://next.api.aliyun.com/api/Dysmsapi/2017-05-25/SendSms?params=%7B%7D&lang=PHP
(2)本人开发用的windows搭的环境,如果想用自己的站点 去对接阿里云短信的同学,注意把本地你自己的域名配个https的,不然请求阿里api时会报curl的错误。
以上准备好后,开始正文:
1. 首先安装一下阿里云的sdk
composer require alibabacloud/dysmsapi-20170525 2.0.9
2. 创建Sample.php,封装阿里云短信api使用
把相关配置项配置在里面,签名和模板没过审的可以先用阿里测试的,具体详情参考阿里的手册
贴一下代码
<?php
namespace app;
use AlibabaCloud\SDK\Dysmsapi\V20170525\Dysmsapi;
use clagiordano\weblibs\configmanager\ArrayConfigManager;
use Darabonba\OpenApi\Models\Config;
use AlibabaCloud\SDK\Dysmsapi\V20170525\Models\SendSmsRequest;
class Sample
{
/**
* 使用AK&SK初始化账号Client
* @param string $accessKeyId
* @param string $accessKeySecret
* @return Dysmsapi Client
*/
public static function createClient($accessKeyId, $accessKeySecret)
{
$config = new Config([
// 您的AccessKey ID
"accessKeyId" => $accessKeyId,
// 您的AccessKey Secret
"accessKeySecret" => $accessKeySecret
]);
// 访问的域名
$config->endpoint = "dysmsapi.aliyuncs.com";
return new Dysmsapi($config);
}
/**
* @param string[] $data
* @return void
*/
public static function main($phone, $code)
{
$code = ["code" => $code];
// $client = self::createClient("LTAI5tJhF874FTXC1RnABC1m", "NNdztlLo3SfJGWjsjX2HrLQWEQWslzT");
$client = self::createClient("LTAI5tFwazjUX3gD3BjLZpVW", "0cHslGgpg2QRlM8Y7R802bici9IEKY"); // 阿里云key和accesecret
$sendSmsRequest = new SendSmsRequest([
"phoneNumbers" => $phone, //控制器层的接收的手机号
"signName" => "阿里云短信测试", //短信签名
"templateCode" => "SMS_12345678", //短信模板
"templateParam" => json_encode($code), //验证码
]);
$resp = $client->sendSms($sendSmsRequest);
if (!$resp) {
return false;
}
return $resp;
}
}
3.所有东西都配置好之后就在想调用的地方调用这个main方法就可以了,我是自己单独封装了一个http的接口,用来发送短信验证码,同时把接收到的验证码存到redis里做一个三分钟的失效时间,用来处理我的业务逻辑,贴一下代码。
/**
* 发送短信验证码
*
* @return Json
*/
public function sendRes(Request $request){
$data['phone'] = $request->param("phone");
$data['create_at'] = date('Y-m-d H:i:s',time());
$data['ip'] = $request->ip();
if (empty($data['phone']) || !LoginService::checkPhoneNum($data['phone'])){
return error_code(10003);
}
//将生成的验证码放入缓存中 做三分钟失效时间
$getCode = rand(1000,9999);
$redis = new Redis(Config::get('cache.stores.redis'));
$redis -> setex($data['phone'],180, $getCode);
//调用阿里api接口 发送验证码
$sendCode = Sample::main($data['phone'],$getCode);
if($sendCode -> body -> code === "isv.BUSINESS_LIMIT_CONTROL"){
return error_code(11103);
}
return json(['code' => 0, 'msg' => '发送成功']);
}
代码里return的error_code是我自己封装的全局错误码,忽略即可。
注:不需要登录注册,只需要对接阿里短信验证的,至此就结束了。
4.注册
贴一下我的mysql表结构
控制器controller层代码:
接收参数 -> 校验 -> 入库
/**
* 注册
*
* @return array|Json
*/
public function register(Request $request)
{
$data['phone'] = $request->post('phone');
$data['username'] = $request->post('username');
$data['pwd'] = $request->post('pwd');
$data['sendSms'] = $request->post('sendSms');
//校验接收数据 -> 入库
$result = LoginService::checkRegister($data);
if($result){
return $result;
}
$sqlData = ['name' => $data['username'],
'pwd' => md5($data['pwd']),
"phone" => $data['phone'],
"lastOpTime" => 0 ,
"regTime" => time()];
Db::name('jason_user')->insert($sqlData);
return json(['code' => 0, 'msg' => '注册成功!']);
}
业务逻辑层service代码:
/**
* 校验用户注册
*
* @return mixed
*/
public static function checkRegister($data)
{
//TODO:注册:手机号为唯一标识,需通过短信验证获得的验证码
$phone = $data['phone'];
$username = $data['username'];
$pwd = $data['pwd'];
$ckSendSms = $data['sendSms'];
$redis = new Redis(Config::get('cache.stores.redis'));
//校验接收到的数据,如果过不了正则,就不必往下继续查mango和redis
if (empty($phone) || empty($username) || empty($pwd) || empty($ckSendSms)){
return error_code(11092);
}
if(!LoginService::checkPhoneNum($phone) ||!LoginService::checkUserName($username) || !LoginService::rexCheckPassword($pwd)){
return error_code(10003);
}
//校验数据库里是否有该手机号
$phoneStatus = Db::table('jason_user')->where('phone', $phone)->find();
if($phoneStatus){
return error_code(11001);
}
//校验短信验证码是否正确
$sendSms = $redis -> get($phone);
if(!$sendSms || $sendSms !== $ckSendSms){
return error_code(11102);
}
return false;
}
5.登录
登录的话我这里采用两种组合方式去登陆,用传过来的操作符operational去区分
operational(1.手机号+密码登录 2.手机号+验证码登录)
控制器controller层代码:
/**
* 登录
*
* @return array|Json
*/
public function login(Request $request){
//TODO:登录两种方式登录
// 用操作符区分 1:密码登录 2:验证码登录
$data['operational'] = $request->post("operational");
$data['phone'] = $request->post("phone");
$data['pwd'] = $request->post("pwd");
$data['sendSms'] = $request->post("sendSms");
$result = LoginService::checkLogin($data);
if($result){
return $result;
}
return json(['code' => 0, 'msg' => '登录成功!']);
}
业务逻辑层service代码:
/**
* 校验用户登录
*
* @return mixed
*/
public static function checkLogin($data)
{
$operational = $data['operational'];
$phone = $data['phone'];
$pwd = $data['pwd'];
$ckSendSms = $data['sendSms'];
$redis = new Redis(Config::get('cache.stores.redis'));
//校验数据库里是否有该手机号
$sqlData = Db::table('jason_user')->where('phone', $phone)->find();
if(!$sqlData){
return error_code(11000);
}
//校验手机号和 密码或验证码
if($operational == 1){
if (empty($phone) || empty($pwd) || !LoginService::checkPhoneNum($phone) || !LoginService::rexCheckPassword($pwd)){
return error_code(10003);
}
//校验密码是否正确
if($sqlData['pwd'] !== md5($pwd)){
return error_code(11002);
}
}else{
if (empty($phone) || empty($ckSendSms) || !LoginService::checkPhoneNum($phone)){
return error_code(10003);
}
//校验短信验证码是否正确
$sendSms = $redis -> get($phone);
if(!$sendSms || $sendSms !== $ckSendSms){
return error_code(11102);
}
}
return false;
}
6.注册和登录 结束。下面贴一下用来校验用户名、手机号、密码的三个正则表达式的方法。
注释写的比较全,如果不符合你的业务场景,请不要copy。
/**
* 验证手机号
*
* @return bool
*/
public static function checkPhoneNum($phone)
{
$isMob = "/^1[3456789]{1}\d{9}$/";
$isTel = "/^([0-9]{3,4}-)?[0-9]{7,8}$/";
if (!preg_match($isMob, $phone) && !preg_match($isTel, $phone)) {
return false;
} else {
return true;
}
}
/**
* 验证密码长度至少为8,且必须包含大小写字母/数字/符号任意三者组合
*
* @return bool
*/
public static function rexCheckPassword($pwd)
{
$regStr = "/^(?![a-zA-Z]+$)(?![A-Z0-9]+$)(?![A-Z\\W_]+$)(?![a-z0-9]+$)(?![a-z\\W_]+$)(?![0-9\\W_]+$)[a-zA-Z0-9\\W_]{8,20}$/";
// $regStr = "/^(?![0-9]+$)(?![a-z]+$)(?![A-Z]+$)(?!([^(0-9a-zA-Z)]|[\(\)])+$)([^(0-9a-zA-Z)]|[\(\)]|[a-z]|[A-Z]|[0-9]){8,}$/";
if(preg_match($regStr, $pwd)){
return true;
}else{
return false;
}
}
/**
* 验证用户名
* [a-zA-Zxa0-xff_]小写大写英文,或者中文,或者下划线开头
* [0-9a-zA-Zxa0-xff_]后面的内容可以是小写大写中文数字下划线
* [0-9a-zA-Zxa0-xff_]{3,15}后面的内容重复3-15次
* @return bool
*/
public static function checkUserName($str)
{
if (preg_match('/^[a-zA-Zxa0-xff_][0-9a-zA-Zxa0-xff_]{3,15}$/', $str)) {
return true;
} else {
return false;
}
}
}
7.至此,发送验证码、注册、登录就结束了,自己随便写的,业务逻辑比较简单。
总结一下就是 先搞定阿里短信验证的对接,然后以手机号作为数据表的唯一标识,去搞注册和登录。
有遇到问题可以评论区dd我,看到会及时回复。