通常我们实名注册用户的时候,都会要求输入手机号,然后获取手机验证码,然后填到页面,像下面这样:

然后把验证码缓存到redis_ViewUI

然后把验证码缓存到redis_ViewUI_02

ok,现在来理一下思绪:

1.注册页面

因为这里要说的是短信验证,所以只贴出核心代码,下面是手机号和短信验证码这两部分的前端代码片段:


<div data-v-7cf0b24e="" class="lj_input_standard4">
    <span data-v-7cf0b24e="" id="left_span6">手机号</span>
    <span data-v-7cf0b24e="" id="asterisk_span6">*</span>
    <input v-model="phone" data-v-7cf0b24e="" id="myinput6" name="data[phone]" type="tel" maxlength="11" placeholder="请输入手机号码">
</div>
<div data-v-7cf0b24e="" class="lj_input_standard4">
    <span data-v-7cf0b24e="" id="left_span7">短信验证码</span>
    <span data-v-7cf0b24e="" id="asterisk_span7">*</span>
    <input data-v-7cf0b24e="" id="myinput7" name="data[code]" type="tel" placeholder="请输入验证码">
    <div data-v-7cf0b24e="" class="lj_register_getvcode sendCode" id="sendCode" @click="send" 
     :class="{ login_code_gbb: (phone.length!==11 || wait) }">{{time}}</div>
</div>


这个页面使用vue.js写的,但是你不用先去了解vue.js再来看接下来的内容,so,继续

上面就是表单的两个字段:phone和code,@click=“send”设置了“获取验证码”这个div的单击事件为send();


//send方法
send: function() {
                if(app.phone.length!=11 || app.phone!=parseInt(app.phone) || app.wait) return;
                $.ajax({
                    url: '/?c=api&m=mobilecode&phone='+app.phone,
                    type: 'post',
                    dataType: 'json',
                    success: function(res) {
                        if( res.code ) {
                            app.timer();
                            layer.msg('发送成功');
                        } else {
                            layer.msg('发送失败');
                        }
                    }
                });
            },


同样的,你可能不懂vue的语法,但是你应该能够看懂:

获取phone字段输入的手机号(经过各种验证之后),并且发起一个ajax请求,正如你看到的那样,这个请求携带了手机号参数,

接下来,继续看请求的接口到底是怎么处理的。

2.api接口


public function mobilecode() {
        $phone = $this->input->get("phone") =='' ? $this->input->post("phone") : $this->input->get("phone");
        if(!$phone){
            exit_json(0,'手机号为空!');
        }
        if($_COOKIE['wait_time'] && $phone == $_SESSION['phone']){
            exit_json(0,'已经发送过短信了,请稍后再试!');
        }

        $randcode = rand('100000','999999');
        $_SESSION['randcode'] = $randcode;
        $_SESSION['phone'] = $phone;
        $this->load->library('mdysms');
        $res = $this->mdysms->sendSms($phone,['code'=>$randcode]);
       if($res->Code === 'OK'){
           //设置cookie
           setcookie("wait_time",1,50);
           $_SESSION['phone'] = $phone;
            //获取方式
           exit_json(1,'短信发送成功!');
       }else{
           exit_json(0,'短信发送失败!');
       }
    }


生成随机验证码并存到服务端session中(把手机号也存到session是为了做校验防止同一手机号多次注册);

加载类文件并执行sendSms方法(自定义类文件,用于将验证码和手机号以及必需参数传给阿里短信服务的SDK方法);

3.自定义类文件

这个类用于收集阿里短信服务SDK方法所需的所有参数,并调用AliSDK中的方法


require_once dirname(__FILE__).'/dysms/SignatureHelper.php';
require_once dirname(__FILE__).'/dysms/conf.inc.php';
use Aliyun\DySDKLite\SignatureHelper;
class Mdysms {
     public $conf;
     
     public function __construct()
     {
         $this->conf['accessKeyId'] = ACCESSKEYID;
         $this->conf['accessKeySecret'] = ACCESSKEYSECRET;
         $this->conf['signName'] = SignName;
         $this->conf['templateCode'] = TemplateCode;
     }

     public function sendSms($phoneNumbers, $param, $templateCode = FALSE, $signName = FALSE)
     {
        $params['TemplateParam'] = json_encode($param, JSON_UNESCAPED_UNICODE);
        $params['TemplateCode'] = $templateCode?$templateCode:$this->conf['templateCode'];
        $params['SignName'] = $signName?$signName:$this->conf['signName'];
        $params['PhoneNumbers'] = $phoneNumbers;

        $helper = new SignatureHelper();

        $resp = $helper->request(
            $this->conf['accessKeyId'],
            $this->conf['accessKeySecret'],
            "dysmsapi.aliyuncs.com",
            array_merge($params, array(
                "RegionId" => "cn-hangzhou",
                "Action" => "SendSms",
                "Version" => "2017-05-25"
            ))
        );
        return $resp;
     }
}


这些红字常量是在conf.inc.php中定义的,是购买阿里短信服务时候获取到的。那么,这个request方法用到的参数如下:


accessKeyId-------------------购买短信通知服务获取
accessKeySecret---------------购买短信通知服务获取
signName----------------------购买短信通知服务获取
templateCode------------------购买短信通知服务获取
PhoneNumbers------------------手机号
TemplateParam-----------------在这里就是随机验证码
domain------------------------dysmsapi.aliyuncs.com
RegionId----------------------cn-hangzhou
Action------------------------SendSms
Version-----------------------2017-05-25


4.阿里云的SDK(其实这个可以无视)


<?php

namespace Aliyun\DySDKLite;

/**
 * 签名助手 2017/11/19
 *
 * Class SignatureHelper
 */
class SignatureHelper {

    /**
     * 生成签名并发起请求
     *
     * @param $accessKeyId string AccessKeyId (https://ak-console.aliyun.com/)
     * @param $accessKeySecret string AccessKeySecret
     * @param $domain string API接口所在域名
     * @param $params array API具体参数
     * @param $security boolean 使用https
     * @return bool|\stdClass 返回API接口调用结果,当发生错误时返回false
     */
    public function request($accessKeyId, $accessKeySecret, $domain, $params, $security=false) {
        $apiParams = array_merge(array (
            "SignatureMethod" => "HMAC-SHA1",
            "SignatureNonce" => uniqid(mt_rand(0,0xffff), true),
            "SignatureVersion" => "1.0",
            "AccessKeyId" => $accessKeyId,
            "Timestamp" => gmdate("Y-m-d\TH:i:s\Z"),
            "Format" => "JSON",
        ), $params);
        ksort($apiParams);

        $sortedQueryStringTmp = "";
        foreach ($apiParams as $key => $value) {
            $sortedQueryStringTmp .= "&" . $this->encode($key) . "=" . $this->encode($value);
        }

        $stringToSign = "GET&%2F&" . $this->encode(substr($sortedQueryStringTmp, 1));

        $sign = base64_encode(hash_hmac("sha1", $stringToSign, $accessKeySecret . "&",true));

        $signature = $this->encode($sign);

        $url = ($security ? 'https' : 'http')."://{$domain}/?Signature={$signature}{$sortedQueryStringTmp}";

        try {
            $content = $this->fetchContent($url);
            return json_decode($content);
        } catch( \Exception $e) {
            return false;
        }
    }

    private function encode($str)
    {
        $res = urlencode($str);
        $res = preg_replace("/\+/", "%20", $res);
        $res = preg_replace("/\*/", "%2A", $res);
        $res = preg_replace("/%7E/", "~", $res);
        return $res;
    }

    private function fetchContent($url) {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_TIMEOUT, 5);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_HTTPHEADER, array(
            "x-sdk-client" => "php/2.0.0"
        ));

        if(substr($url, 0,5) == 'https') {
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        }

        $rtn = curl_exec($ch);

        if($rtn === false) {
            trigger_error("[CURL_" . curl_errno($ch) . "]: " . curl_error($ch), E_USER_ERROR);
        }
        curl_close($ch);

        return $rtn;
    }
}