腾讯云人脸核身之独立H5接入

  项目需求:要求在H5页面中接入第三方人脸识别,人脸识别校验通过后再进行后续业务流程。

 

  需求调研

    经调研,阿里云目前不存在一个统一的接口实现“人证合一校验”,需要自己根据其api接口搭建业务场景。
  阿里云"人证合一校验"文档:https://help.aliyun.com/document_detail/186171.html?spm=a2c4g.11186623.6.897.49a36d10QNAVAj

 

  最终决定采用腾讯云“人脸核身”独立H5接入方式实现。

  

  腾讯云“人脸核身”独立H5接入文档:https://cloud.tencent.com/document/product/1007/35883


  腾讯云“人脸核身”实现步骤如下:

  1. 自助接入,申请申请WBappid,获取测试WBappid供开发调试使用;
  2. 获取 Access Token、获取 SIGN ticket、生成签名;
  3. 合作方后台上送身份信息;
  4. 获取 Access Token、获取 NONCE ticket、生成签名;
  5. 启动 H5 人脸核身;
  6. 获取 Access Token、获取 SIGN ticket、合作方后台生成签名;
  7. 服务端验证结果,调用身份认证查询接口;
  8. 实现人脸核身通过后续业务逻辑。

  

  代码实现

 一、合作方后台上送身份信息

  获取 Access Token、获取 SIGN ticket、生成签名

  https://cloud.tencent.com/document/product/1007/35883

 

  获取 Access Token

public  function getToken()
    {
        $access_token = Cache::get('tencentyun_access_token_service');

        // 如果缓存中没有access_token,则向微信服务器请求
        if (empty($access_token)) {
            $url = 'https://idasc.webank.com/api/oauth2/access_token?app_id=' . $this->app_id . '&secret=' . $this->secret  . '&grant_type=client_credential&version=1.0.0';
            $str = curl_get($url);
            $result = !empty($str) ? json_decode($str, true) : [];

            if (!empty($result['access_token'])) {
                Cache::set('tencentyun_access_token_service', $result['access_token'], 6800); // 因为 API ticket 依赖于 Access Token,所以生命周期最长为 3600秒。为了简单方便,建议将 API ticket 与 Access Token 绑定,每20分钟定时刷新,原 API ticket 1 小时(3600秒)失效
                $access_token = $result['access_token'];
            }
        }
        return $access_token;
    }

  获取 SIGN ticket

public function getSignTicket($access_token)
    {
        // 换成中获取sign_ticket
        $sign_ticket = Cache::get('tencentyun_sign_ticket_service');


        // 如果缓存中没有sign_ticket,则向微信服务器请求
        if (empty($sign_ticket)) {
            $url = 'https://idasc.webank.com/api/oauth2/api_ticket?app_id=' . $this->app_id . '&access_token=' . $access_token. '&type=SIGN&version=1.0.0';
            $str = curl_get($url);
            $result = !empty($str) ? json_decode($str, true) : [];

            if (!empty($result['tickets'][0]['value'])) {
                Cache::set('tencentyun_sign_ticket_service', $result['tickets'][0]['value'], 3000); // 因为 API ticket 依赖于 Access Token,所以生命周期最长为 3600秒。为了简单方便,建议将 API ticket 与 Access Token 绑定,每20分钟定时刷新,原 API ticket 1 小时(3600秒)失效
                $sign_ticket = $result['tickets'][0]['value'];
            }
        }

        return $sign_ticket;
    }

  生成签名

public function sign($orderNo='', $idNo='', $name='', $sign_ticket='', $userId='')
    {
        // 版本号
        $version = '1.0.0';

        $list[] = $this->app_id;
        $list[] = $orderNo;
        $list[] = $name;
        $list[] = $idNo;
        $list[] = $userId;
        $list[] = $version;

        $sign = $this->getSigh($list, $sign_ticket);

        return $sign;

    }

  合作方后台上送身份信息

public function geth5faceid($userVo)
    {
        $tencentFaceidService = new TencentFaceidService();

        // 获取accessToken
        $access_token = $tencentFaceidService->getToken();
        // 获取signTicket
        $sign_ticket = $tencentFaceidService->getSignTicket($access_token);

        // 合作方上送计算签名
        $sign = $tencentFaceidService->sign($userVo['orderNo'], $userVo['idNo'], $userVo['name'], $sign_ticket, $userVo['userId']);
        $userVo['sign'] = $sign;

        // 合作方上送身份信息
        $url = 'https://idasc.webank.com/api/server/h5/geth5faceid';
        $json = $tencentFaceidService->curlPost($url, $userVo, $timeOut = 8);

        $geth5faceid = json_decode($json, true);

        return $geth5faceid;

    }

 二、启动 H5 人脸核身

  https://cloud.tencent.com/document/product/1007/35884

  获取 Access Token(同上)、获取 NONCE ticket

  • 前置条件:请合作方确保 Access Token 已经正常获取。
  • NONCE ticket 是合作方前端包含 App 和 H5 等生成签名鉴权参数之一,启动 H5 或 SDK 人脸核身。
  • API ticket 的 NONCE 类型,其有效期为120秒,且一次性有效,即每次启动 SDK 刷脸都要重新请求 NONCE ticket。
public function getNonceTicket($access_token, $userId)
    {
        // 向微信服务器请求
        $url = 'https://idasc.webank.com/api/oauth2/api_ticket?app_id=' . $this->app_id . '&access_token=' . $access_token. '&type=NONCE&version=1.0.0&user_id=' . $userId;
        $str = curl_get($url);
        $result = !empty($str) ? json_decode($str, true) : [];

        if (!empty($result['tickets'][0]['value'])) {
            $nonce_ticket = $result['tickets'][0]['value'];
        }

        return $nonce_ticket;
    }

  启动人脸核身生成签名

public function faceSign($orderNo, $userId, $nonce_ticket, $h5faceId, $nonce)
    {
        $list[] = $this->app_id;
        $list[] = $orderNo;
        $list[] = $userId;
        $list[] = '1.0.0';
        $list[] = $h5faceId;
        $list[] = $nonce;

        $sign = $this->getSigh($list, $nonce_ticket);
        return $sign;
    }

  启动人脸核身,获取启动链接

public function startCheckFace()
    {
        $param = request()->param();

        // 前端传参:客户身份证号、客户姓名、from(App || browser)

        // 校验参数
        $validate = new \think\Validate([
            'idNo' => 'require|idCard',
            'name' => 'require',
            'url' => 'require',
        ]);

        $validate->message([
            'idNo.require' => '身份证号码不能为空',
            'idNo.idCard' => '身份证号码不合法',
            'name.require' => '姓名不能为空',
            'url.require' => '跳转地址不能为空',
        ]);

        if (!$validate->check($param)) $this->error($validate->getError());

        $userVo['idNo'] = $param['idNo'];
        $userVo['name'] = $param['name'];
        $from = 'App';// 默认APP

        $tencentFaceidService = new TencentFaceidService();

        // 随机生成32位唯一用户ID和订单ID
        $userId = $tencentFaceidService->getUuid();
        $orderNo = $tencentFaceidService->getUuid();

        $userVo['webankAppId'] = config('Tencentyun.app_id');
        $userVo['userId'] = $userId;
        $userVo['orderNo'] = $orderNo;
        $userVo['version'] = '1.0.0';

        // 调起人脸核身链接
        $requestUrl = '';

        try {
            // 获取accessToken
            $access_token = $tencentFaceidService->getToken();
            // 上送合作方用户信息
            $geth5faceid = $this->geth5faceid($userVo);

            // 如果签名不合法,需要清空$access_token、$sign_ticket
            if ($geth5faceid['code'] == '400101') {

                Cache::set('tencentyun_access_token_service', null);
                Cache::set('tencentyun_sign_ticket_service', null);
                $this->error("签名不合法,请重试");
            }

            if ($geth5faceid['code'] != 0) {
                $this->error($geth5faceid['msg']);
            }

            // 获取h5/geth5faceid 接口返回的唯一标识
            $h5faceId = $geth5faceid['result']['h5faceId'];

            // 获取32位随机数
            $nonce = $tencentFaceidService->getUuid();

            // 获取nonceTicket
            $nonce_ticket = $tencentFaceidService->getNonceTicket($access_token, $userId);

            // 启动人脸核身计算签名
            $sign = $tencentFaceidService->faceSign($orderNo, $userId, $nonce_ticket, $h5faceId, $nonce);

            // 成功拉起人脸识别并识别成功或失败后的回调路径,前端提供校验完成后回调地址,注意区分不同环境域名    (H5 人脸核身结果跳转)
            if (strpos($param['url'], '?')) {// 已经有参数
                $path = config('share.BASE_URL') . '/h5' . $param['url'] . '&checked=1';
            } else {
                $path = config('share.BASE_URL') . '/h5' . $param['url'] . '?checked=1';
            }

            $url = urlencode($path);

            $requestUrl = "https://ida.webank.com/api/web/login?webankAppId=" . $userVo['webankAppId'] . "&version=" . $userVo['version'] . "&nonce=" . $nonce . "&orderNo=" . $orderNo . "&h5faceId=" . $h5faceId . "&url=" . $url . "&userId=" . $userId . "&sign=" . $sign . "&from=" . $from;


        } catch (Exception $e) {

            $this->error("启动人脸核身异常,异常原因如下:". $e->getMessage());
        }

        $this->success($requestUrl);
    }

// 生成uuid
public function getUuid()
    {
        $chars = md5(uniqid(mt_rand(), true));
        $uuid = substr ( $chars, 0, 8 ) .
            substr ( $chars, 8, 4 ) .
            substr ( $chars, 12, 4 ) .
            substr ( $chars, 16, 4 ) .
            substr ( $chars, 20, 12 );
        return $uuid ;
    }

 成功唤起示例

 

 

 三、服务端验证结果

  https://cloud.tencent.com/document/product/1007/35889

  独立H5人脸核身提供了前端和服务端两种获取验证结果的方式,此处只阐述服务器端获取方式。

  获取 Access Token、获取 SIGN ticket、合作方后台生成签名、服务端验证结果

public function serverCheckResult()
    {
        $param = request()->param();

        // 校验参数
        $validate = new \think\Validate([
            'orderNo' => 'require',
        ]);

        $validate->message([
            'orderNo.require' => '订单号不能为空',
        ]);

        if (!$validate->check($param)) $this->error($validate->getError());

        $orderNo = $param['orderNo'];

        // app_id
        $app_id = config('Tencentyun.app_id');

        $tencentFaceidService = new TencentFaceidService();

        // 获取accessToken
        $access_token = $tencentFaceidService->getToken();

        // 获取signTicket
        $sign_ticket = $tencentFaceidService->getSignTicket($access_token);

        // 获取32位随机数
        $nonceStr = $tencentFaceidService->getUuid();

        // 服务端验证结果签名
        $sign = $tencentFaceidService->checkServerSign($orderNo, $nonceStr, $sign_ticket);

        // 合作方上送身份信息
        $url = "https://idasc.webank.com/api/server/sync?app_id={$app_id}&nonce={$nonceStr}&order_no={$orderNo}&version=1.0.0&sign={$sign}&get_file=''";
        $json = curl_get($url, $timeOut = 8);

        $json = json_decode($json, true);

        if ($json['code'] != 0) {
            $this->error($json['msg']);
        }

        $this->success($json['result']);
    }


// 服务端验证结果合作方后台生成签名
public function checkServerSign($orderNo='', $nonceStr='', $sign_ticket='')
    {
        // 版本号
        $version = '1.0.0';

        $list[] = $this->app_id;
        $list[] = $orderNo;
        $list[] = $version;
        $list[] = $nonceStr;

        $sign = $this->getSigh($list, $sign_ticket);

        return $sign;

    }

 

 四、人脸识别成功后续操作

  ……此处省略一万字……

  

  注意:上线正式环境前需要替换正式WBappid。

  

  java用户请参考链接:https://blog.csdn.net/HanSong_Ai/article/details/105760849

 

 

 

 

posted @ 2021-03-09 16:48  巫毒  阅读(2223)  评论(0)    收藏  举报