文章目录
- 一、概述
- 二、合作方后台上送身份信息~实现流程
- 2.1. 前端入参
- 2.2. 后端固定参数
- 2.3. 获取 Access Token
- 2.4. 获取 SIGN ticket
- 2.5. 生成签名
- 2.6. 合作方后台上送身份信息
- 三、启动H5人脸核身
- 3.1. 获取h5faceId
- 3.2. 获取nonce
- 3.3. 获取nonceTicket
- 3.4. 计算启动签名
- 3.5. 构建回调页面链接
- 四、查询核身结果
- 4.1. 启动H5人脸核身
一、概述
人脸识别,使用官方API:腾讯云人脸核身之独立H5接入。接口官方返回code = 0 表示成功,其他code码值均为对应码值信息,详见错误码。
注意:
1.合作方上送身份信息的计算签名参数与启动人脸核身计算签名参数不一致,有部分区别。
2.wbappid = webankAppId = app_id
二、合作方后台上送身份信息~实现流程
2.1. 前端入参
前端入参:客户身份证号、客户姓名、用户 ID (userId)、from(App || browser)
controller
@Autowiredprivate 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);}
entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class FaceDetectUserVO {// https://cloud.tencent.com/document/product/1007/35893private String name;//姓名private String idNo;//证件号码private String userId;//用户 ID ,用户的唯一标识(不能带有特殊字符),需要跟生成签名的 userId 保持一致private String from;//来源 App || browser)
}
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*/@Overridepublic String getAccessTokenTencent() {// 从redis中获取accessTokenTencentString 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_tokenaccessTokenTencent = jsonMap.get("access_token");// 过期时间 默认7200L 设置6800L提前重新获取redisUtils.set("accessTokenTencent", accessTokenTencent, 6800L);}log.info("返回有效accessToken,为:[{}]", accessTokenTencent);return accessTokenTencent;}
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*/@Overridepublic String getSignTicketTencent(String accessTokenTencent) {// 从redis中获取nonceTicketTencentString 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;}
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);}
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*/@Overridepublic TXH5IdentityInfoDTO sendH5IdentityInfoUserInfo(FaceDetectUserVO faceDetectUserVO) {//获取accessTokenString accessToken = commonIdentityService.getAccessTokenTencent();//获取signTicketString 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;}
三、启动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;}
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
接好后,直接将该链接返回前端去打开即可拉起人脸核身。请注意,该链接仅一次有效!!!
/*** 构造人脸核身获取启动链接* 文档:https://cloud.tencent.com/document/product/1007/35894** @param faceDetectUserVO* @return*/@Overridepublic GraceJSONResult startCheckFace(FaceDetectUserVO faceDetectUserVO) {//随机生成32位唯一用户ID和订单IDString userId = SignUtils.GenerateRandom32Number(32);String orderNo = SignUtils.GenerateRandom32Number(32);faceDetectUserVO.setOrderNo(orderNo);faceDetectUserVO.setUserId(userId);String requestUrl = "";try {//获取accessTokenString 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);//获取nonceTicketString 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);}
四、查询核身结果
4.1. 启动H5人脸核身
/*** 前端获取结果验证签名* API:https://cloud.tencent.com/document/product/1007/61302** @param orderNo 订单号,字母/数字组成的字符串,本次人脸核身合作伙伴上送的订单号,唯一标识* @return*/private String getCheckSign(String orderNo) {//获取accessTokenString accessToken = commonIdentityService.getAccessTokenTencent();//获取signTicketString 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;}