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


    一、概述

    人脸识别,使用官方API:腾讯云人脸核身之独立H5接入。接口官方返回code = 0 表示成功,其他code码值均为对应码值信息,详见错误码
    注意:
    1.合作方上送身份信息的计算签名参数与启动人脸核身计算签名参数不一致,有部分区别。
    2.wbappid = webankAppId = app_id

    二、合作方后台上送身份信息~实现流程
    2.1. 前端入参

    前端入参:客户身份证号、客户姓名、用户 ID (userId)、from(App || browser)
    controller

        @Autowired
        private PCH5SendIdentityService pch5SendIdentityService;
    
        /**
         * 合作方后台上送身份信息 PC H5
         * 文档:https://cloud.tencent.com/document/product/1007/35893
         * <p>
         * 请求 URL:https://miniprogram-kyc.tencentcloudapi.com/api/server/h5/geth5faceid?orderNo=xxx
         * 请求方法:POST
         * 报文格式:Content-Type: application/json
         * </p>
         *
         * @param faceDetectUserVO 身份信息
         */
        @PostMapping("/sendH5IdentityInfoUserInfo")
        public TXH5IdentityInfoDTO sendH5IdentityInfoUserInfo(@RequestBody FaceDetectUserVO faceDetectUserVO) {
            return pch5SendIdentityService.sendH5IdentityInfoUserInfo(faceDetectUserVO);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    entity

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class FaceDetectUserVO {
    
        // https://cloud.tencent.com/document/product/1007/35893
        private String name;//姓名
        private String idNo;//证件号码
        private String userId;//用户 ID ,用户的唯一标识(不能带有特殊字符),需要跟生成签名的 userId 保持一致
        private String from;//来源 App || browser)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    2.2. 后端固定参数

    后端固定参数:wbappid = webankAppId = app_id(API中介绍命名不同,注意)、orderNo(可自定义随机生成不唯一)、userId(可自定义随机生成不唯一)、version

    2.3. 获取 Access Token

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

     /**
         * 获取 access_token
         * 文档: https://cloud.tencent.com/document/product/1007/37304
         *
         * @return
         */
        @Override
        public String getAccessTokenTencent() {
    
            // 从redis中获取accessTokenTencent
            String accessTokenTencent = redisUtils.get("accessTokenTencent");
            log.info("获取redis中的accessToken,为:[{}]", accessTokenTencent);
    
            if (StringUtils.isEmpty(accessTokenTencent)) {
                String accessTokenUrl = String.format(TencentCloudConfig.ACCESS_TOKEN_URL, appId, secret);
                String jsonStr = HttpUtil.doGet(accessTokenUrl, null);
    
                log.info("返回报文;->{}", jsonStr);
                Map<String, String> jsonMap = ConvertUtils.stringToMap(jsonStr);
    
                if (!"0".equals(jsonMap.get("code"))) {
                    String msg = jsonMap.get("msg");
                    log.error("获取腾讯token信息错误,code:{},msg:{}", jsonMap.get("code"), msg);
                    GraceJSONResult.errorMsg(msg);
                    /**
                     * 错误响应示例:
                     * {
                     *     "code": "66660000",
                     *     "msg": "请求参数异常",
                     *     "bizSeqNo": "22090720001184453210262184859700",
                     *     "transactionTime": "20220907102621",
                     *     "success": false,
                     *     "expire_in": 0
                     * }
                     */
                }
                /**
                 * 正确响应示例:
                 * {
                 *  "code":"0","msg":"请求成功",
                 *  "transactionTime":"20151022043831",
                 *  "access_token":"accessToken_string",
                 *  "expire_time":"20151022043831",
                 *  "expire_in":"7200"
                 * }
                 */
                // 获取 access_token
                accessTokenTencent = jsonMap.get("access_token");
    
                // 过期时间 默认7200L  设置6800L提前重新获取
                redisUtils.set("accessTokenTencent", accessTokenTencent, 6800L);
            }
    
            log.info("返回有效accessToken,为:[{}]", accessTokenTencent);
            return accessTokenTencent;
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    2.4. 获取 SIGN ticket

    https://cloud.tencent.com/document/product/1007/57613
    通过token获取signTicket

     /**
         * 获取 SIGN ticket
         * 请求地址: http://localhost:9900/getSignTicketTencent
         * 文档: https://cloud.tencent.com/document/product/1007/37305
         *
         * @param accessTokenTencent access_token
         * @return
         */
        @Override
        public String getSignTicketTencent(String accessTokenTencent) {
            // 从redis中获取nonceTicketTencent
            String signTicketTencent = redisUtils.get("signTicketTencent");
            log.info("获取redis中的signTicketTencent,为:[{}]", signTicketTencent);
    
            String signTicketValue = null;
            if (StringUtils.isEmpty(signTicketTencent)) {
                String getSignTicketUrl = String.format(TencentCloudConfig.SIGN_TICKET_URL, appId, accessTokenTencent);
                String jsonStr = HttpUtil.doGet(getSignTicketUrl, null);
                log.info("返回报文;->{}", jsonStr);
    
                TicketDTO ticketDTO = JSON.parseObject(jsonStr, TicketDTO.class);
                if (!"0".equals(ticketDTO.getCode())) {
                    String msg = ticketDTO.getMsg();
                    log.error("获取腾讯signTicket信息错误,code:{},msg:{}", ticketDTO.getCode(), msg);
                    GraceJSONResult.errorMsg(msg);
                }
    
                /**
                 * 正确响应示例:
                 * {
                 *       "code": "0",
                 *       "msg": "请求成功",
                 *       "transactionTime": "20151022044027",
                 *       "tickets": [
                 *         {
                 *              "value": "ticket_string",
                 *              "expire_in": "3600",
                 *              "expire_time": "20151022044027"
                 *         }
                 *     ]
                 * }
                 */
                signTicketValue = ticketDTO.getTickets().get(0).getValue();
                // 过期时间 默认3600L  设置3200L提前重新获取
                redisUtils.set("signTicketTencent", signTicketValue, 3000L);
            }
    
            return signTicketValue;
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    2.5. 生成签名

    计算合作方上送身份信息签名,参数有:wbappid、orderNo、name、idNo、userId、version、signTicket
    在这里插入图片描述
    计算签名

      /**
         * PC 端 H5 接入 > 合作方上送身份信息计算签名
         * 文档地址:https://cloud.tencent.com/document/product/1007/35893
         *
         * @param faceDetectUserVO
         * @param signTicket
         * @return
         */
        public String signH5(FaceDetectUserVO faceDetectUserVO, String signTicket) {
    
            //为计算签名做准备
            //为计算签名做准备
            List<String> list = new ArrayList<>();
            list.add(appId);
            list.add(faceDetectUserVO.getOrderNo());
            list.add(faceDetectUserVO.getName());
            list.add(faceDetectUserVO.getIdNo());
            list.add(faceDetectUserVO.getUserId());
            list.add(TencentCloudConfig.VERSION);
            return SignUtils.getSign(list, signTicket);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    2.6. 合作方后台上送身份信息
    /**
         * 合作方后台上送身份信息 PC H5
         * 文档:https://cloud.tencent.com/document/product/1007/35893
         * <p>
         * 请求 URL:https://miniprogram-kyc.tencentcloudapi.com/api/server/h5/geth5faceid?orderNo=xxx
         * 请求方法:POST
         * 报文格式:Content-Type: application/json
         * </p>
         *
         * @param faceDetectUserVO 身份信息
         * @return
         */
        @Override
        public TXH5IdentityInfoDTO sendH5IdentityInfoUserInfo(FaceDetectUserVO faceDetectUserVO) {
            //获取accessToken
            String accessToken = commonIdentityService.getAccessTokenTencent();
            //获取signTicket
            String signTicket = commonIdentityService.getSignTicketTencent(accessToken);
            //订单号
            String orderNo = SignUtils.GenerateRandom32Number(32);
            faceDetectUserVO.setOrderNo(orderNo);
            //合作方上送计算签名
            String sign = signH5(faceDetectUserVO, signTicket);
    
            Map<String, String> param = new HashMap<>(16);
            param.put("webankAppId", appId);
            param.put("orderNo", orderNo);
            param.put("name", faceDetectUserVO.getName());
            param.put("idNo", faceDetectUserVO.getIdNo());
            param.put("userId", faceDetectUserVO.getUserId());
            param.put("version", TencentCloudConfig.VERSION);
            param.put("sign", sign);
    
            log.debug("合作方上送身份信息参数有:[{}]", param);
            String getFaceidUrl = String.format(TencentCloudConfig.GET_H5_FACEID_URL, orderNo);
            String jsonStr = HttpUtil.doPost(getFaceidUrl, JSON.toJSONString(param));
            log.info("返回报文;->{}", jsonStr);
    
            TXH5IdentityInfoDTO txh5IdentityInfoDTO = JSON.parseObject(jsonStr, TXH5IdentityInfoDTO.class);
            log.info("合作方上送身份信息接口返回:[{}]", txh5IdentityInfoDTO);
            return txh5IdentityInfoDTO;
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    三、启动H5人脸核身
    3.1. 获取h5faceId

    在合作方成功上送身份信息后,可以获取到h5faceId

    3.2. 获取nonce

    (32位随机数)

    3.3. 获取nonceTicket

    获取nonceTicket(通过token & userId)

    3.4. 计算启动签名

    https://cloud.tencent.com/document/product/1007/61074
    计算启动H5人脸核身签名,参数有:wbappid、orderNo、userId、version、h5faceId、nonce、nonceTicket
    在这里插入图片描述

       /**
         * 启用 H5 人脸认证 人脸核身计算签名
         * 文档:https://cloud.tencent.com/document/product/1007/35894
         *
         * @param orderNo     订单号,字母/数字组成的字符串,本次人脸验证合作伙伴上送的订单号,唯一标识
         * @param userId      用户 ID ,用户的唯一标识(不要带有特殊字符)
         * @param nonceTicket 合作伙伴服务端实时获取的 tikcet,注意是 NONCE 类型
         * @param h5faceId    h5/geth5faceid 接口返回的唯一标识
         * @param nonce       随机数:32位随机串(字母+数字组成的随机数)
         * @return
         */
        private String faceSignH5(String orderNo, String userId, String nonceTicket, String h5faceId, String nonce) {
    
            //为计算签名做准备
            List<String> list = new ArrayList<>();
            list.add(appId);
            list.add(orderNo);
            list.add(userId);
            list.add(TencentCloudConfig.VERSION);
            list.add(h5faceId);
            list.add(nonce);
            String sign = SignUtils.getSign(list, nonceTicket);
            log.info("启动人脸核身返回签名为:[{}]", sign);
            return sign;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    3.5. 构建回调页面链接

    将成功拉起人脸核身验证通过后的回调页面链接配置至配置文件,同时对该链接进行encode编码
    获取到所有拉起人脸核身所需参数后,向链接https://ida.webank.com/api/web/login拼接上参数:webankAppId、version、nonce、orderNo、h5faceId、url、sign、from、userId。例如:

    https://ida.webank.com/api/web/login?webankAppId=%s&version=1.0.0&nonce=%s&orderNo=%s&h5faceId=%s&url&userId=%s&sign=%s&from=%s
    
    • 1

    接好后,直接将该链接返回前端去打开即可拉起人脸核身。请注意,该链接仅一次有效!!!

    
        /**
         * 构造人脸核身获取启动链接
         * 文档:https://cloud.tencent.com/document/product/1007/35894
         *
         * @param faceDetectUserVO
         * @return
         */
        @Override
        public GraceJSONResult startCheckFace(FaceDetectUserVO faceDetectUserVO) {
            //随机生成32位唯一用户ID和订单ID
            String userId = SignUtils.GenerateRandom32Number(32);
            String orderNo = SignUtils.GenerateRandom32Number(32);
            faceDetectUserVO.setOrderNo(orderNo);
            faceDetectUserVO.setUserId(userId);
            String requestUrl = "";
            try {
    
                //获取accessToken
                String accessToken = commonIdentityService.getAccessTokenTencent();
                //上送合作方用户信息
                TXH5IdentityInfoDTO txh5IdentityInfoDTO = sendH5IdentityInfoUserInfo(faceDetectUserVO);
    
                if (!"0".equals(txh5IdentityInfoDTO.getCode())) {
                    String msg = txh5IdentityInfoDTO.getMsg();
                    log.info("启动人脸核身--上送合作方用户信息异常,异常原因为:[{}]]", msg);
                    GraceJSONResult.errorMsg(msg);
                }
    
                //获取h5/geth5faceid 接口返回的唯一标识
                String h5faceId = txh5IdentityInfoDTO.getResult().getH5faceId();
    
                //获取32位随机数
                String nonce = SignUtils.GenerateRandom32Number(32);
    
                //获取nonceTicket
                String nonceTicket = commonIdentityService.getNonceTicketTencent(accessToken, userId);
    
                //启动人脸核身计算签名
                String sign = faceSignH5(orderNo, userId, nonceTicket, h5faceId, nonce);
    
                //成功拉起人脸识别并识别成功或失败后的回调路径
                String oauthCallback = TencentCloudConfig.OAUTH_CALLBACK_URL;
                log.debug("人脸核身通过后的回调地址-拼接路径加密前:url = [{}]", oauthCallback);
    
                String oauthRedirectUrl = URLEncoder.encode(oauthCallback, "utf-8");
                log.debug("人脸核身通过后的回调地址-拼接路径加密后:url = [{}]", oauthRedirectUrl);
    
                /**
                 * https://miniprogram-kyc.tencentcloudapi.com/api/pc/login?webankAppId=appId001
                 * &version=1.0.0
                 * &nonce=4bu6a5nv9t678m2t9je5819q46y9hf93
                 * &orderNo=161709188560917432576916585
                 * &h5faceId=wb04f10695c3651ce155fea7070b74c9
                 * &url=https%3a%2f%2fcloud.tencent.com
                 * &userId=23333333333333
                 * &sign=5DD4184F4FB26B7B9F6DC3D7D2AB3319E5F7415F
                 */
                requestUrl = String.format(TencentCloudConfig.REQUEST_URL, appId, nonce, orderNo, h5faceId, oauthRedirectUrl, userId, sign, faceDetectUserVO.getFrom());
            } catch (Exception e) {
                log.error("启动人脸核身异常,异常原因为:[{}]", e.getMessage());
            }
            log.info("启动人脸核身--请求路径为:[{}]]", requestUrl);
            return GraceJSONResult.ok(requestUrl);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    四、查询核身结果
    4.1. 启动H5人脸核身

    在这里插入图片描述
    在这里插入图片描述

        /**
         * 前端获取结果验证签名
         * API:https://cloud.tencent.com/document/product/1007/61302
         *
         * @param orderNo 订单号,字母/数字组成的字符串,本次人脸核身合作伙伴上送的订单号,唯一标识
         * @return
         */
        private String getCheckSign(String orderNo) {
            //获取accessToken
            String accessToken = commonIdentityService.getAccessTokenTencent();
    
            //获取signTicket
            String signTicket = commonIdentityService.getSignTicketTencent(accessToken);
            List list = new ArrayList<>();
            list.add(appId);
            list.add(orderNo);
            list.add(TencentCloudConfig.VERSION);
            list.add(SignUtils.GenerateRandom32Number(32));
            String sign = SignUtils.getSign(list, signTicket);
            log.info("前端获取结果验证签名值为\"[{}]", sign);
            return sign;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
  • 相关阅读:
    金仓数据库 KingbaseGIS 使用手册(4. 数据管理和查询)
    iOS 16 SwiftUI列表(List)项包含 Label 视图导致分隔线变短的解决
    Mybatis源码解析(六):一级缓存和二级缓存的优先级
    数据校验(深入篇)
    chatgpt的命令词
    基础生态学名词解释
    百度文心一言
    阿里云容器镜像服务的运维总结
    使用nginx-lua配置统一url自动跳转到hadoop-ha集群的active节点
    Node-RED系列教程-27node-red操作邮件节点
  • 原文地址:https://blog.csdn.net/weixin_40816738/article/details/126779209