腾讯云图形验证码的PHP示例

需要准备的

复制代码
1.API密钥
SecretId 及 SecretKey 两部分,
SecretId 用于标识 API 调用者的身份,
SecretKey 用于加密签名字符串和服务器端验证签名字符串的密钥。
前往API密钥管理页面,即可进行获取 
https://console.cloud.tencent.com/cam/capi

2. 图形验证码凭证
CaptchaAppId 是需要客户端页面传入,为了给后端进行校验
AppSecretKey 是密钥用,不可暴露在客户端,主要是后端进行调用
验证码密钥是构建腾讯验证码请求的重要凭证
前往验证码管理页面,即可进行
https://console.cloud.tencent.com/captcha/graphical?fromCode=true


下载证书

复制代码
证书问题
如果您的 PHP 环境证书有问题,可能会遇到报错,类似于cURL error 60: See http://curl.haxx.se/libcurl/c/libcurl-errors.html
请尝试按以下步骤解决:
到 https://curl.haxx.se/ca/cacert.pem 下载证书文件cacert.pem,将其保存到 PHP 安装路径下。
编辑php.ini文件,删除curl.cainfo配置项前的分号注释符(;),
值设置为保存的证书文件cacert.pem的绝对路径。
重启依赖 PHP 的服务。

开启php_curl

注:代码是基于 tp5.0来的,只是一个参考

复制代码
此 SDK 依赖的 GuzzleHttp 需要开启 php_curl 扩展,查看环境上的 php.ini 环境确认是否已启用

安装SDK包

shell 复制代码
composer require tencentcloud/tencentcloud-sdk-php

官网SDK调试

网址:https://console.cloud.tencent.com/api/explorer?Product=captcha&Version=2019-07-22&Action=DescribeCaptchaResult&SignVersion=

现阶段需要修改的

1.注册 {host}/index/index/reg.html
2.找回密码 {host}/index/index/find_password.html

配置项

根据tp5.0框架特性,扩展配置文件直接放入application/extra目录会自动加载
路径:application/extra/imgvalidate.php

php 复制代码
<?php
  return [
  	'captchaAppid' => '登录到腾讯云平台去拿',   //每个医院的值需要配置下的
  	'captchaAppkey' => '登录到腾讯云平台去拿',
  	'secretId' => 'AKIDObZHismPkoc9OM3UdV6RVIn31i8HAl6H',   //这是已经设定好了的
  	'secretKey' => '4tSkmSs10jAKDqBF2ZomTu4xa1FCsHQI',
  	'region' => 'ap-shanghai'
  ];

调用sdk封装类

路径:extend/sms/ImgValidate.php

php 复制代码
<?php

  namespace sms;

use TencentCloud\Captcha\V20190722\CaptchaClient;
use TencentCloud\Captcha\V20190722\Models\DescribeCaptchaResultRequest;
use TencentCloud\Common\Profile\ClientProfile;
use TencentCloud\Common\Profile\HttpProfile;
use TencentCloud\Common\Credential;
use think\Exception;

class ImgValidate
{
  private $_secretId; //平台API的密钥id
  private $_secretKey;
  private $_captAppid; //短信发送方的appid
  private $_captKey;
  private $_region;   //区域

  public function __construct($imgValidConf)
  {
    $this->_initConf($imgValidConf);
  }

  private function _initConf($imgValidConf)
  {
    if (empty($imgValidConf)) {
      exception('腾讯云配置数据不能为空!', 9000);
    }
    if (empty($imgValidConf['secretId'])) {
      exception('平台API的secretId不能为空!',9001);
    }
    if (empty($imgValidConf['secretKey'])) {
      exception('平台API的secretKey不能为空!',9002);
    }
    if (empty($imgValidConf['captchaAppid'])) {
      exception('平台API的captAppid不能为空!', 9003);
    }
    if (empty($imgValidConf['captchaAppkey'])) {
      exception('平台API的captKey不能为空!',9004);
    }

    $this->_secretId = $imgValidConf['secretId'];
    $this->_secretKey = $imgValidConf['secretKey'];
    $this->_captAppid = $imgValidConf['captchaAppid'];
    $this->_captKey = $imgValidConf['captchaAppkey'];
    $this->_region = $imgValidConf['region'];
  }

  /**
     * 从前端传过来的必要参数
     * @param $ticket //用户在客户端验证成功的字符吗
     * @param $randStr //用户在客户端验证完成的随机码
     */
  public function sendCode($ticket, $randStr)
  {
    try {
      $cred = new Credential($this->_secretId, $this->_secretKey);
      $httpProfile = new HttpProfile();
      $httpProfile->setEndpoint("captcha.tencentcloudapi.com");

      $clientProfile = new ClientProfile();
      $clientProfile->setHttpProfile($httpProfile);
      $client = new CaptchaClient($cred, "", $clientProfile);

      $req = new DescribeCaptchaResultRequest();

      $params = array(
        "CaptchaType" => 9,
        "Ticket" => $ticket,
        "UserIp" => get_client_ip(),
        "BusinessId" => 1,
        "SceneId" => 1,
        "Randstr" => $randStr,
        "CaptchaAppId" => intval($this->_captAppid),
        "AppSecretKey" => $this->_captKey
        // "NeedGetCaptchaTime" => 1
      );
      $req->fromJsonString(json_encode($params));
      $res = $client->DescribeCaptchaResult($req);

      $resJson = $res->toJsonString();

      $resData = json_decode($resJson, true);
      if ($resData['CaptchaCode'] == 1) {
        return true;
      } else {
        wLog($resJson, $allowIpInfo = null, $level = 'jsonmsg-error', $logPath = 'tencentImgValid');
        return false;
      }
    } catch (Exception  $e) {
      $logInfo = [
        'code' => $e->getCode(),
                'msg' => $e->getMessage()
            ];
            wLog(var_export($logInfo, true), $allowIpInfo = null, $level = '腾讯云图形验证码异常:', 'tencentImgValid');

            return false;
        }
    }
}

ctroller层调用

调用的API:

路径:application/api/controller/Imgvalidate.php

php 复制代码
<?php

  namespace app\api\controller;

use think\Exception;

class Imgvalidate
{
  /**
     * 腾讯云的图形验证码操作
     * @return false|string
     */
  public function dovalidate()
  {
    $randStr = Input('post.randstr');
    $ticket = Input('post.ticket');
    $account = input('post.account');

    $imgConf = config('imgvalidate');
    try {
      $ImgValidate = new \sms\ImgValidate($imgConf);
      $res = $ImgValidate->sendCode($ticket,$randStr);

      if (!$res) {
        $returnData = [
          'code'=>500,
          'msg'=>'failed'
          ];
      } else {
        //用来做token校验
        $cacheKey = 'img_validate_'.$account;
        $token = getImgvaldateToken($cacheKey);
        cache($cacheKey,$token);
        wLog($token, $allowIpInfo = null, $level = 'token-'.$cacheKey, $logPath = 'imgValidToken');

        //记录当前时间
        //                $currentTime = time();
        //                cache('dovalid_time_'.$account, $currentTime);

        $returnData = [
          'code'=>200,
          'data' => cache($cacheKey),
          'msg' => 'success'
          ];
      }
      return json($returnData);

    } catch (Exception $e) {
      $logInfo = [
        'code' => $e->getCode(),
        'msg' => $e->getMessage()
        ];
      wLog(var_export($logInfo,true),null,'腾讯云图形验证码异常:','tencentImgValid');
      return json(['code'=>501,'msg'=>'failed']);
    }
  }
}

短信发送前的校验:

在application/api/controller/User.php加入检验token的代码

php 复制代码
/**
     * 校验token
     * @param $account //手机号码或用户名
     * @param $postSign
     * @param $beforCheck
     * @return array
     */
private function _checkSign($account, $postSign, $beforCheck=0)
  {
    if ($beforCheck == 1) {  //是否需要检查用户信息是否存在
       $Users=new Users();
       $res = $Users->check_account($account);
       if (!empty($res)) {
         $returnData = ['code'=>0,'msg'=>'用户数据已经存在'];
         return $returnData;
       }
    }

    //获取在腾讯图形验证码生成时生成的token
    $cacheKey = 'img_validate_'.$account;
    $localSign = cache($cacheKey);  

    if (!$localSign) {
      $returnData = ['code' => 0, 'msg' => '请求非法,请联系管理员'];
    } else {
      if ($postSign == $localSign) {
        $returnData = ['code'=>1,'msg'=>'校验成功!'];
        cache($cacheKey,null);  //校验成功后把这个session删除掉。
        //                wLog($postSign, $allowIpInfo = null, $level = 'validSucc-'.$cacheKey, $logPath = 'imgValidSucc');
      } else {
        wLog($postSign, $allowIpInfo = null, $level = 'postsign-'.$cacheKey, $logPath = 'imgValidFaild');
        $returnData = ['code'=>0,'msg'=>'校验失败!'];
      }
    }
    return $returnData;
  }

//发送短信校验码
public function send_verify()
  {
    $ip=$_SERVER["REMOTE_ADDR"];//ip
    $id = 1;
    $param = input('post.param');
    $uv_r = input('post.uv_r');
    $postSign = input('post.data');
    $beforChk = input('post.beforchk',0);
    $type = 'sms';
    $tel = $param;//电话

    //检查请求是否非法
    $checkData = $this->_checkSign($tel, $postSign, $beforChk);
    if ($checkData['code'] != 1) {
      return json($checkData);
    }

    //判断数据是否超过了限制
    //        $uvr_num = $this->checkUvr($uv_r);
    $tel_num = $this->checkTel($tel);

    ....
  }

在注册、找回密码渲染页controller层加入appid

路径:application/index/controller/Index.php

php 复制代码
namespace app\index\controller;


use app\common\model\Announcement;
use app\common\model\AnnouncementMiddle;
use app\common\model\Branch;
use app\common\model\ImagesHistoryModel;
use app\common\model\ImagesMilieuModel;
use app\common\model\Jobs;

use app\common\service\ConfigServer;
use think\Db;

class Index extends Base
{
  ....

  //会员注册
  public function reg() {
    $this->set_seo('会员注册');
    $imgConf = config('imgvalidate');   //获取扩展配置

    $this->assign('appid',$imgConf['captchaAppid']);    //前端页面需要用到appid
    return $this->fetch();
  }

  //找回密码
  public function find_password(){
    $this->set_seo('找回密码');
    $imgConf = config('imgvalidate');

    $this->assign('appid',$imgConf['captchaAppid']);

    return  $this->fetch();
  }
  ....

}

添加公共方法

在调用接口时,需要记录日志信息,方便来排查接口的错误信息,具体是在sdk封装类中使用

路径:application/common.php

php 复制代码
/**
 * 日志写入文件
 * @param $msg  日志内容
 * @param null $allowIpInfo 允许写入的ip
 * @param string $level     层级
 * @param string $logPath   日志路径
 */
function wLog($msg, $allowIpInfo = null, $level = 'msg', $logPath = '')
{
  //授权给某个ip才能记录日志
  if (!empty($allowIpInfo)) {

    \think\Log::key(\think\Request::instance()->ip());  //获取当前的ip设置给日志

    if (is_array($allowIpInfo)) {
      $logConfig['allow_key'] = $allowIpInfo;
    } elseif (is_string($allowIpInfo)) {
      $logConfig['allow_key'] = [$allowIpInfo];
    } else {   //有可能ip经过转换

           }
  }

  $logPath = !empty($logPath) ? 'log/'.$logPath : 'log';
  $lastPath = RUNTIME_PATH .$logPath . DS;
  if (!is_dir($lastPath)) {
    mkdir($lastPath,0777,true);
  }

  $logConfig = [
    'type' => 'File',                          //日志记录方式,内置 file socket 支持扩展
    'path' => $lastPath,                        //日志保存目录
    'file_size' => 2097152,                     //文件到了这个大小,自动生成下一个文件
    'level' => [$level],                         //日志记录级别
    'time_format' => 'c',
    ];

  //记录日志
  \think\Log::init($logConfig);
  \think\Log::record($msg, $level);
}

/**
 * 图形验证码中设置token,用来校验
 * @param $data
 * @param string $prefix
 */
function getImgvaldateToken($data,$prefix='hospitalname') 
{
  $data = $prefix ? $prefix.$data : $data;

  return md5(md5($data));
}

前端步骤

前端地址 https://cloud.tencent.com/document/product/1110/36841

javascript 复制代码
1.引用微信官方的js包 
  <script src="https://ssl.captcha.qq.com/TCaptcha.js"></script>


2. 发送验证码时候,data-appid,data-cbfn,data-biz-state不能为空
  <a href="javascript:;" class="btn-send" data-appid="{$appid}" data-cbfn="callbackTentent" data-biz-state="data-biz-state" onclick="checkSmscode(this)">发送验证码</a>

3. 根据官方文档开启验证图层显示
var appId = String({$appid});
var captcha = new TencentCaptcha(appId,callbackTentent);
captcha.show(); // 显示验证码

具体代码

javascript 复制代码
/**
 * 获得腾讯图形验证码
 * @param obj
 */
function checkSmscode(obj) {
  var account=$(".find_account").val();
  if(!account){
    layer.msg('手机号不能为空!', {icon: 5});
  }else{
    //直接生成一个验证码对象。
    try {
      var appId = String({$appid});
      var captcha = new TencentCaptcha(appId,callbackTentent);
      captcha.show(); // 显示验证码
    } catch (error) {
      loadErrorCallback(error);
    }
  }
}

//图形验证码回调
function callbackTentent(res) {
  if (res.ret === 0) {
    var account = $(".find_account").val();
    var uv_r = $(".uv_r").val();

    $.ajax({
      type: "POST",
      url: "/api/imgvalidate/dovalidate",
      data: {'ticket': res.ticket, 'randstr': res.randstr, 'account': account},
      dataType: "json",
      success: function (checkRes) {
        // checkRes = JSON.parse(checkRes);
        var obj = $(".btn-send");

        if (checkRes['code'] == '200') {
          var wait=60;
          var timeId;
          time(obj);

          //验证码
          $.ajax({
            url: "/api/user/send_verify",
            type: 'POST',
            data: {id: 1, param: account, type: 'sms', uv_r: uv_r, data:checkRes['data']},
            dataType: 'JSON',
            success: function (ret) {
              if (ret.code == 200) {
                var wait=60;
                var timeId;
                time(obj);
                layer.msg(ret.msg,{icon:1});
              } else {
                layer.msg(ret.msg, {icon: 5});
              }
            }
          });
        } else {
          layer.msg('校验失败了,请联系管理员', {icon: 5});
        }
      }
    });
  } else {
    layer.msg('加载验证码失败了,请联系管理员',{icon:5});
  }
}

/**
 * 抛出异常错误
 * @param err
 */
function loadErrorCallback(err) {
  console.log(err);
}

补充:

前端最好做成封装的,方便调用。

相关推荐
两个人的幸福3 天前
Windows 桌面应用自研 PHP 队列(下):完整代码与六大工程化优化
php
BingoGo5 天前
PHP 泛型之殇 泛型 RFC 提案被拒绝
后端·php
JaguarJack5 天前
PHP 泛型之殇 泛型 RFC 提案被拒绝
后端·php
用户3074596982076 天前
PHP 扩展——从入门到理解
php
鹏仔先生7 天前
拷贝漫画APP下载页PHP程序,后台带免费AI写作
php
云水一下7 天前
从零开始学 PHP 系列(一):PHP 的前世今生与开发环境搭建
开发语言·php
xingpanvip7 天前
星盘接口开发文档:本命盘接口指南
android·开发语言·css·php·lua
酉鬼女又兒7 天前
零基础入门计算机网络运输层:端到端通信核心作用、端口号分类规则、复用分用工作机制及UDP与TCP协议全方位对比详解
网络·网络协议·tcp/ip·计算机网络·考研·udp·php
dog2507 天前
不要再继续优化 TCP
网络协议·tcp/ip·php
Channing Lewis7 天前
PHP 解析 Excel 的那些坑:一次“行号错位”引发的数据丢失
开发语言·php·excel