腾讯云图形验证码的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);
}

补充:

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

相关推荐
BingoGo2 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php
JaguarJack2 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php·服务端
BingoGo3 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php
JaguarJack3 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php·服务端
JaguarJack4 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
后端·php·服务端
BingoGo4 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
php
JaguarJack5 天前
告别 Laravel 缓慢的 Blade!Livewire Blaze 来了,为你的 Laravel 性能提速
后端·php·laravel
郑州光合科技余经理5 天前
代码展示:PHP搭建海外版外卖系统源码解析
java·开发语言·前端·后端·系统架构·uni-app·php
QQ5110082855 天前
python+springboot+django/flask的校园资料分享系统
spring boot·python·django·flask·node.js·php
WeiXin_DZbishe5 天前
基于django在线音乐数据采集的设计与实现-计算机毕设 附源码 22647
javascript·spring boot·mysql·django·node.js·php·html5