赞
踩
配置实名认证服务、实现实名认证
序
首先这里的接入方案是标准H5方案,目前看来,微信还没有对百度智能云的这个实名认证服务做技术壁垒,但是不排除LM的小马哥做这种骚操作。如果微信浏览器里要跳到外置浏览器做这个实名认证过程,那就体验不爽了。
官方的接入步骤与文档地址自行百度。
官方时序图:
我这边进行的封装与解耦时序图。各个公司可以有自己的配置,我们的平台仅仅是做服务抽象与解耦,只需要各公司提供配置即可。
这里先要看百度云的接入步骤,理解它的设计思路,然后才能在它的思路上做服务封装(任何第三方服务想要平台化都是这个思路)
这边是希望各个公司自行申请百度云服务授权,然后我们再业务系统记录授权信息。公司业务在做服务请求时,通过配置的授权信息与百度云做交互,作为一个中间的纽带。我这边是springCloud微服务架构,对业务模块做了拆解,剖析分析提供以下服务支持,
第三方服务模块提供直接接口
第三方服务service层接口代码
import cn.hutool.core.map.MapUtil; import cn.hutool.core.net.URLEncodeUtil; import cn.hutool.json.JSONUtil; import com.baidubce.http.ApiExplorerClient; import com.baidubce.http.HttpMethodName; import com.baidubce.model.ApiExplorerRequest; import com.baidubce.model.ApiExplorerResponse; import com.fillersmart.fsihouse.data.common.api.ResponseCodeI18n; import com.fillersmart.fsihouse.data.core.Result; import com.fillersmart.fsihouse.data.core.ResultGenerator; import com.fillersmart.fsihouse.data.vo.thirdapi.face.request.BaiduVerifyRequest; import com.fillersmart.fsihouse.data.vo.thirdapi.face.verfity.FaceVerifyVo; import com.fillersmart.fsihouse.thirdservice.controller.util.BaiduFaceUtil; import java.util.Map; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author zhengwen **/ @Slf4j @RestController @RequestMapping("/faceVerfity") public class FaceVerfityController { /** * 获取AccessToken * * @param baiduVerifyRequest 百度实名request * @return 统一出参 */ @PostMapping(value = "/getBaiduAccessToken") public Result<?> getBaiduAccessToken(@RequestBody BaiduVerifyRequest baiduVerifyRequest) { //参数校验 Result<?> res = BaiduFaceUtil.checkGetAccessTokenParam(baiduVerifyRequest); if (res != null) { return res; } String path = "https://aip.baidubce.com/oauth/2.0/token"; ApiExplorerRequest request = new ApiExplorerRequest(HttpMethodName.POST, path); // 设置header参数 request.addHeaderParameter("Content-Type", "application/json;charset=UTF-8"); // 设置query参数 request.addQueryParameter("client_id", baiduVerifyRequest.getApiKey()); request.addQueryParameter("client_secret", baiduVerifyRequest.getSecretKey()); request.addQueryParameter("grant_type", "client_credentials"); // 设置jsonBody参数 String jsonBody = "{}"; if (MapUtil.isNotEmpty(baiduVerifyRequest.getJsonBody())) { jsonBody = JSONUtil.toJsonStr(baiduVerifyRequest.getJsonBody()); } request.setJsonBody(jsonBody); ApiExplorerClient client = new ApiExplorerClient(); try { ApiExplorerResponse response = client.sendRequest(request); // 返回结果格式为Json字符串 return ResultGenerator.genSuccessResult(response.getResult()); } catch (Exception e) { log.error("百度智能云获取AccessToken异常,原因:{}", e.getMessage(), e); return ResultGenerator.genFailResult(ResponseCodeI18n.GET_TOKEN_FAILED.getMsg()); } } /** * 获取验证token * * @param faceVerifyVo 人脸验证vo * @return 统一出参 */ @PostMapping(value = "/getBaiduVerifyToken") public Result<?> getBaiduVerifyToken(@RequestBody FaceVerifyVo faceVerifyVo) { String path = "https://aip.baidubce.com/rpc/2.0/brain/solution/faceprint/verifyToken/generate"; ApiExplorerRequest request = new ApiExplorerRequest(HttpMethodName.POST, path); // 设置header参数 request.addHeaderParameter("Content-Type", "application/json;charset=UTF-8"); //转换百度请求request Result<?> res = BaiduFaceUtil.checkAndAddVerifyTokenParam(faceVerifyVo, request); if (res != null) { return res; } ApiExplorerClient client = new ApiExplorerClient(); try { ApiExplorerResponse response = client.sendRequest(request); // 返回结果格式为Json字符串 return ResultGenerator.genSuccessResult(response.getResult()); } catch (Exception e) { log.error("--获取百度人脸verifyToken异常,原因:{}", e.getMessage(), e); return ResultGenerator.genFailResult(ResponseCodeI18n.GET_TOKEN_FAILED.getMsg()); } } /** * 指定用户认证(指定用户信息上报) * * @param faceVerifyVo 人脸验证vo * @return 统一出参 */ @PostMapping(value = "/appointUserCertBaidu") public Result<?> appointUserCertBaidu(@RequestBody FaceVerifyVo faceVerifyVo) { //String path = "https://aip.baidubce.com/solution/faceprint/idcard/submit"; String path = "https://brain.baidu.com/solution/faceprint/idcard/submit"; ApiExplorerRequest request = new ApiExplorerRequest(HttpMethodName.POST, path); //转换百度请求request Result<?> res = BaiduFaceUtil.checkAppointUserCertParam(faceVerifyVo, request); if (res != null) { return res; } // 设置header参数 request.addHeaderParameter("Content-Type", "application/json;charset=UTF-8"); ApiExplorerClient client = new ApiExplorerClient(); try { ApiExplorerResponse response = client.sendRequest(request); // 返回结果格式为Json字符串 return ResultGenerator.genSuccessResult(response.getResult()); } catch (Exception e) { log.error("--指定用户认证信息异常,原因:{}", e.getMessage(), e); return ResultGenerator.genFailResult(ResponseCodeI18n.USER_CERT_FAIL.getMsg()); } } /** * 获取实名认证url * * @param faceVerifyVo 人脸验证vo * @return 统一出参 */ @PostMapping(value = "/getFaceCertUrlBaidu") public Result<?> getFaceCertUrlBaidu(@RequestBody FaceVerifyVo faceVerifyVo) { //参数校验 Result<?> res = BaiduFaceUtil.checkGetFaceCertUrlParam(faceVerifyVo); if (res != null) { return res; } String verifyToken = faceVerifyVo.getVerifyToken(); if (StringUtils.isBlank(verifyToken)) { //accessToken String accessToken = faceVerifyVo.getAccessToken(); if (StringUtils.isBlank(accessToken)) { BaiduVerifyRequest baiduVerifyRequest = BaiduFaceUtil.initBaiduVerifyRequest(faceVerifyVo); res = getBaiduAccessToken(baiduVerifyRequest); res = BaiduFaceUtil.transSetAccessToken(res, faceVerifyVo); if (res != null) { return res; } } //verifyToken //先取verifyToken res = getBaiduVerifyToken(faceVerifyVo); res = BaiduFaceUtil.transSetVerityToken(res, faceVerifyVo); if (res != null) { return res; } verifyToken = faceVerifyVo.getVerifyToken(); //指定用户认证 res = appointUserCertBaidu(faceVerifyVo); res = BaiduFaceUtil.transSetAppointUserResult(res, faceVerifyVo); if (res != null) { return res; } } Map<String, String> certAfterInfo = faceVerifyVo.getCertAfterInfo(); String successUrl = certAfterInfo.get("successUrl"); String failUrl = certAfterInfo.get("failUrl"); StringBuffer urlSbf = new StringBuffer(); urlSbf.append("https://brain.baidu.com/face/print/?token="); urlSbf.append(verifyToken); urlSbf.append("&successUrl=").append(URLEncodeUtil.encodeAll(successUrl)); urlSbf.append("&failedUrl=").append(URLEncodeUtil.encodeAll(failUrl)); faceVerifyVo.setCertUrl(urlSbf.toString()); //TODO 是否要避免重复请求呢?是否需要存库呢? return ResultGenerator.genSuccessResult(faceVerifyVo); } /** * 获取实名认证结果 * * @param faceVerifyVo 人脸验证vo * @return 统一出参 */ @PostMapping(value = "/getFaceCertResultBaidu") public Result<?> getFaceCertResultBaidu(@RequestBody FaceVerifyVo faceVerifyVo) { //参数校验 String path = "https://aip.baidubce.com/rpc/2.0/brain/solution/faceprint/result/detail"; ApiExplorerRequest request = new ApiExplorerRequest(HttpMethodName.POST, path); //转换百度请求request Result<?> res = BaiduFaceUtil.checkGetFaceCertResultParam(faceVerifyVo, request); if (res != null) { return res; } ApiExplorerClient client = new ApiExplorerClient(); try { ApiExplorerResponse response = client.sendRequest(request); // 返回结果格式为Json字符串 res = ResultGenerator.genSuccessResult(response.getResult()); } catch (Exception e) { log.error("--获取人脸身份实名认证结果异常,原因:{}", e.getMessage(), e); res = ResultGenerator.genFailResult(ResponseCodeI18n.GET_CERT_RESULT_FAIL.getMsg()); } return res; } }
第三方接口层代码
import com.alibaba.fastjson.JSONObject; import com.fillersmart.fsihouse.data.common.api.ResponseCodeI18n; import com.fillersmart.fsihouse.data.constant.ConstantsEnum; import com.fillersmart.fsihouse.data.core.RedisUtil; import com.fillersmart.fsihouse.data.core.Result; import com.fillersmart.fsihouse.data.core.ResultCode; import com.fillersmart.fsihouse.data.core.ResultGenerator; import com.fillersmart.fsihouse.data.vo.thirdapi.face.request.BaiduVerifyRequest; import com.fillersmart.fsihouse.data.vo.thirdapi.face.verfity.FaceVerifyVo; import com.fillersmart.fsihouse.thirdweb.controller.util.FaceVerfityUtil; import com.fillersmart.fsihouse.thirdweb.service.FaceVerfityRpcService; import javax.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author zhengwen **/ @Slf4j @RestController @RequestMapping("/faceVerfity") public class FaceVerfityController { @Resource private RedisUtil redisUtil; @Resource private FaceVerfityRpcService faceVerfityRpcService; /** * 获取AccessToken * * @param faceVerifyVo 人脸识别入参vo * @return 统一出参 */ @PostMapping(value = "/getAccessToken") public Result<?> getAccessToken(@RequestBody FaceVerifyVo faceVerifyVo) { Result<?> res = ResultGenerator.genFailResult(ResponseCodeI18n.GET_TOKEN_FAILED.getMsg()); String supportName = faceVerifyVo.getSupportName(); if (StringUtils.isBlank(supportName)) { return ResultGenerator.genFailResult(ResponseCodeI18n.SERVICE_SUPPORT_NULL.getMsg()); } ConstantsEnum.ServiceSupportType support = ConstantsEnum.ServiceSupportType.getEnum( supportName); switch (support) { case BAIDU: //先从缓存取 res = FaceVerfityUtil.getBaiduAccessTokenFromCache(redisUtil, faceVerifyVo); if (res == null) { BaiduVerifyRequest baiduVerifyRequest = FaceVerfityUtil.transToBaiduVerfityRequest( faceVerifyVo); res = faceVerfityRpcService.getBaiduAccessToken(baiduVerifyRequest); res = FaceVerfityUtil.transBaiduRespToResult(res, faceVerifyVo, redisUtil); } break; default: break; } return res; } /** * 获取验证token * * @param faceVerifyVo 人脸验证vo * @return 统一出参 */ @PostMapping(value = "/getVerifyToken") public Result<?> getVerifyToken(@RequestBody FaceVerifyVo faceVerifyVo) { Result<?> res = ResultGenerator.genFailResult(ResponseCodeI18n.GET_TOKEN_FAILED.getMsg()); String supportName = faceVerifyVo.getSupportName(); if (StringUtils.isBlank(supportName)) { return ResultGenerator.genFailResult(ResponseCodeI18n.SERVICE_SUPPORT_NULL.getMsg()); } ConstantsEnum.ServiceSupportType support = ConstantsEnum.ServiceSupportType.getEnum( supportName); switch (support) { case BAIDU: //先从缓冲取 res = FaceVerfityUtil.getBaiduAccessTokenFromCache(redisUtil, faceVerifyVo); if (res == null) { res = getAccessToken(faceVerifyVo); } if (res != null && res.getCode().equals(ResultCode.SUCCESS.code())) { res = faceVerfityRpcService.getBaiduVerifyToken(faceVerifyVo); res = FaceVerfityUtil.transBaiduVerifyTokenResp(res, faceVerifyVo); } break; default: break; } return res; } /** * 指定用户认证(指定用户信息上报) * * @param faceVerifyVo 人脸验证vo * @return 统一出参 */ @PostMapping(value = "/appointUserCert") public Result<?> appointUserCert(@RequestBody FaceVerifyVo faceVerifyVo) { Result<?> res = ResultGenerator.genFailResult(ResponseCodeI18n.USER_CERT_FAIL.getMsg()); String supportName = faceVerifyVo.getSupportName(); if (StringUtils.isBlank(supportName)) { return ResultGenerator.genFailResult(ResponseCodeI18n.SERVICE_SUPPORT_NULL.getMsg()); } ConstantsEnum.ServiceSupportType support = ConstantsEnum.ServiceSupportType.getEnum( supportName); switch (support) { case BAIDU: res = faceVerfityRpcService.appointUserCertBaidu(faceVerifyVo); //校验转换统一出参 res = FaceVerfityUtil.transBaiduAppointUserCertResp(res, faceVerifyVo); break; default: break; } return res; } /** * 获取实名认证url * * @param faceVerifyVo 人脸验证vo * @return 统一出参 */ @PostMapping(value = "/getFaceCertUrl") public Result<?> getFaceCertUrl(@RequestBody FaceVerifyVo faceVerifyVo) { //参数校验 Result<?> res = FaceVerfityUtil.checkGetFaceCertUrlParam(faceVerifyVo); if (res != null) { return res; } //先取verifyToken res = getVerifyToken(faceVerifyVo); if (res != null && res.getCode().equals(ResultCode.FAIL.code())) { return res; } else { faceVerifyVo = JSONObject.parseObject(JSONObject.toJSONString(res.getData()), FaceVerifyVo.class); } //指定用户认证 res = appointUserCert(faceVerifyVo); if (res != null && res.getCode().equals(ResultCode.FAIL.code())) { return res; } else { faceVerifyVo = JSONObject.parseObject(JSONObject.toJSONString(res.getData()), FaceVerifyVo.class); } String supportName = faceVerifyVo.getSupportName(); ConstantsEnum.ServiceSupportType support = ConstantsEnum.ServiceSupportType.getEnum( supportName); switch (support) { case BAIDU: res = faceVerfityRpcService.getFaceCertUrlBaidu(faceVerifyVo); break; default: break; } return res; } /** * 获取实名认证结果 * * @param faceVerifyVo 人脸验证vo * @return 统一出参 */ @PostMapping(value = "/getFaceCertResult") public Result<?> getFaceCertResult(@RequestBody FaceVerifyVo faceVerifyVo) { //参数校验 Result<?> res = FaceVerfityUtil.checkGetFaceCertResultParam(faceVerifyVo); if (res != null) { return res; } String supportName = faceVerifyVo.getSupportName(); ConstantsEnum.ServiceSupportType support = ConstantsEnum.ServiceSupportType.getEnum( supportName); switch (support) { case BAIDU: res = faceVerfityRpcService.getFaceCertResultBaidu(faceVerifyVo); //转换结果 FaceVerfityUtil.transBaiduCertResultRespToResult(res, faceVerifyVo); break; default: break; } return res; } }
在接口层是做了将service层返回的数据做转换未业务的统一出参的,用户服务层提供的2个接口的代码就不能分享了,各位可以根据我画的时序图自行实现。实际上里面也就是做了公司配置的查询,再组合调用第三方服务模块提供的接口,postman的接口请求示例倒是可以分享下,大家可以根据入参结合时序图理解实现这2个接口。
{
"authInfo": {
"apiKey": "百度智能云apiKey", //apiKey 必传
"secretKey": "百度智能云secretKey" //secretKey 必传
},
"supportName": "baidu" //第三方服务费,必传
}
{
"authInfo": {
"apiKey": "百度智能云apiKey", //apiKey 必传
"secretKey": "百度智能云secretKey" //secretKey 必传
},
"accessToken": "", //鉴权token,非必传,不传,先取缓存,缓存没有内部会主动去取
"planId": 新建的H5小程序方案ID, //方案ID,必传
"supportName": "baidu" //第三方服务费,必传
}
{
"authInfo": {
"apiKey": "百度智能云apiKey", //apiKey 必传
"secretKey": "百度智能云secretKey" //secretKey 必传
},
"certUserInfo": {
"idName": "xxx", //姓名,必传
"idNo": "42900身份证", //证件号码,必传
"certificateType": 0 //证件类型:非必传,0大陆居民二代身份证1港澳台居民来往内地通行证2外国人永久居留证3定居国外的中国公民护照
},
"verifyToken":"接口2返回的verifyToken",
"accessToken": "", //鉴权token,非必传,不传,先取缓存,缓存没有内部会主动去取
"planId": 新建的H5小程序方案ID, //方案ID,必传
"supportName": "baidu" //第三方服务费,必传
}
{ "authInfo": { "apiKey": "xxxx", //apiKey 必传 "secretKey": "xxxx" //secretKey 必传 }, "certUserInfo": { "idName": "xx", //姓名,必传 "idNo": "xxx", //证件号码,必传 "certificateType": 0 //证件类型:非必传,0大陆居民二代身份证1港澳台居民来往内地通行证2外国人永久居留证3定居国外的中国公民护照 }, "certAfterInfo": { "successUrl": "https://blog.csdn.net/", //认证成功后跳转地址,必传。这里建议传业务url,从哪里进的跳回哪里 "failUrl": "https://blog.csdn.net/zwrlj527" //认证失败跳转地址,必传 }, "verifyToken": "", //验证token,非必传,不传会自行取,要自己传就通过前面的接口获取 "accessToken": "", //鉴权token,非必传,不传,先取缓存,缓存没有内部会主动去取 "planId": , //方案ID,必传 "supportName": "baidu" //第三方服务费,必传 }
{{apiUrl}}/faceVerfity/getFaceCertResult
{
"verifyToken": "xxx", //验证token,非必传,不传会自行取,要自己传就通过前面的接口获取
"accessToken": "xxx", //鉴权token,非必传,不传,先取缓存,缓存没有内部会主动去取
"supportName": "baidu" //第三方服务费,必传
}
以上每一步的入参对象都是环环相扣,上一步的出参可以作为下一步的入参。
用户模块的接口
1、 租户发起实名认证
{{apiUrl}}/rent/user/rentUserIdCodeCert
{
"rentUserDto": {
"id": 1151, //租户id,必传
"custName": "xxx", //租户姓名,必传
"certType": 1, //认证类型,必传, 1 身份证 2 学生证 3 军官证 4 驾驶证 5护照 6港澳通行证 7外国人永久居留证
"certCode": "xxxx", //身份号,必传
"orgId": 60 //公司id,必传
},
"faceVerifyVo": {
"certAfterInfo": {
"successUrl": "https://blog.csdn.net/", //认证成功后跳转地址,必传。这里建议传业务url,从哪里进的跳回哪里
"failUrl": "https://blog.csdn.net/zwrlj527" //认证失败跳转地址,必传
}
}
}
2、获取租户实名认证结果
{{apiUrl}}/rent/user/getRentUserCertResult
{ "rentUserDto": { "id": 1151, //租户id,必传 "custName": "xx", //租户姓名,必传 "certType": 1, //认证类型,必传, 1 身份证 2 学生证 3 军官证 4 驾驶证 5护照 6港澳通行证 7外国人永久居留证 "certCode": "xxx", //身份号,必传 "orgId": 60 //公司id,必传 }, "faceVerifyVo": { //获取人脸实名认证url返回的data对象 "accessToken": "xxx", "authInfo": { "secretKey": "xxx", "apiKey": "xx" }, "certAfterInfo": { "successUrl": "https://blog.csdn.net/", "failUrl": "https://blog.csdn.net/zwrlj527" }, "certMatch": null, "certResult": null, "certUrl": "https://brain.baidu.com/face/print/?token=xxxx&successUrl=https%3A%2F%2Fblog.csdn.net%2F&failedUrl=https%3A%2F%2Fblog.csdn.net%2Fzwrlj527", "certUserInfo": { "idName": "xxx", "idNo": "xxxx" }, "planId": xx, "supportName": "baidu", "verifyToken": "xxx" } }
2个接口的入参是相扣的,接口1的出参人脸认证对象,直接可以整个在扔进去,其实也是为了方便,前端是需要做简单的组合就行了。
好了,就分享到这里,希望能帮到各位博友。最后愿世界和平,疫情早日消除,同呼吸共命运。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。