公司有直播的需求,选择了对接腾讯的sdk(吐槽一下,腾讯的文档写得是真的垃圾,但是功能倒是挺全的)
人脸核身: 用户首次使用时需要申请直播的开播权限,腾讯刚好有人脸核身的服务,需要在腾讯平台申请信息
由于我们使用的是appsdk 所以需要看的是appsdk 增强版的文档
大概流程是需要由服务端生成并下发参数,客户端获取参数后启动sdk,进行人脸核身,sdk返回人脸核身结果,服务端验证人脸结果
代码:
人脸核身sdk的信息,也就是申请sdk时的信息
@Component
@ConfigurationProperties(prefix = "tencent-faceid")
public class TencentFaceIdInfo {
// 腾讯人脸核身的访问api
private String bizTokenUrl;
//腾讯人脸核身sdkwbappid
private String WBappid;
//腾讯人脸核身sdk Secret
private String secret;
private String keyLicense;
}
Nacos 上的配置
人脸核身工具类
/**
* @author hzl
* 人脸核身
*/
@Component
@Slf4j
public class TencentFaceIdClient {
@Resource
private TencentFaceIdInfo tencentFaceIdInfo;
@Resource
private TencentLiveInfo tencentLiveInfo;
private static String ACCESS_TOKEN_URL = "https://miniprogram-kyc.tencentcloudapi.com/api/oauth2/access_token";
private static String TICKET_URL = "https://miniprogram-kyc.tencentcloudapi.com/api/oauth2/api_ticket";
private static String SUCCESS = "0";
/**
* 获取accessToken
* @return
* @throws Exception
*/
public String getAccessToken() throws Exception {
HashMap<String,Object> paramMap = new HashMap<>();
paramMap.put("app_id",tencentFaceIdInfo.getWBappid());
paramMap.put("secret",tencentFaceIdInfo.getSecret());
paramMap.put("grant_type","client_credential");
paramMap.put("version","1.0.0");
String result = HttpUtil.get(ACCESS_TOKEN_URL, paramMap);
JSONObject object = JSONObject.parseObject(result);
String code = object.getString("code");
if (!SUCCESS.equals(code)){
throw new BizException("请求AccessToken失败");
}
return object.getString("access_token");
}
/**
* 获取SignTicket
* @return
* @throws Exception
*/
public String getSignTicket(String accessToken) throws Exception {
HashMap<String,Object> paramMap = new HashMap<>();
paramMap.put("app_id",tencentFaceIdInfo.getWBappid());
paramMap.put("access_token",accessToken);
paramMap.put("type","SIGN");
paramMap.put("version","1.0.0");
String result = HttpUtil.get(TICKET_URL, paramMap);
JSONObject object = JSONObject.parseObject(result);
String code = object.getString("code");
if (!SUCCESS.equals(code)){
throw new BizException("请求SignTicket失败");
}
return object.getJSONArray("tickets").getJSONObject(0).getString("value");
}
/**
* 获取ApiTicket
* @return
* @throws Exception
*/
public String getApiTicket(String accessToken,String userId) throws Exception {
HashMap<String,Object> paramMap = new HashMap<>();
paramMap.put("app_id",tencentFaceIdInfo.getWBappid());
paramMap.put("access_token",accessToken);
paramMap.put("type","NONCE");
paramMap.put("version","1.0.0");
paramMap.put("user_id",userId);
String result = HttpUtil.get(TICKET_URL, paramMap);
JSONObject object = JSONObject.parseObject(result);
String code = object.getString("code");
if (!SUCCESS.equals(code)){
throw new BizException(“请求ApiTicket失败");
}
return object.getJSONArray("tickets").getJSONObject(0).getString("value");
}
/**
* 生成签名
* @param values
* @param ticket
* @return
*/
public static String sign(List<String> values, String ticket) {
if (values == null) {
throw new NullPointerException("values is null");
}
values.removeAll(Collections.singleton(null));
values.add(ticket);
java.util.Collections.sort(values);
StringBuilder sb = new StringBuilder();
for (String s : values) {
sb.append(s);
}
return Hashing.sha1().hashString(sb, Charsets.UTF_8).toString().toUpperCase();
}
/**
* 获取签名
* @param
* @param ticket
* @return
*/
public String getSign(String wbappid,String userId,String version,String ticket,String nonce){
List<String> values = new ArrayList<String>();
values.add(wbappid);
values.add(userId);
values.add(version);
values.add(nonce);
String sign = sign(values, ticket);
return sign;
}
public String getFaceId(FaceIdReq req){
// Map<String, Object> paramMap = BeanUtil.beanToMap(req);
String paramStr = JSONObject.toJSONString(req);
HttpResponse response = HttpUtil.createPost("https://miniprogram-kyc.tencentcloudapi.com/api/server/getfaceid")
.body(paramStr).contentType("application/json").execute();
String responseBody = response.body();
JSONObject object = JSONObject.parseObject(responseBody);
String code = object.getString("code");
if (!SUCCESS.equals(code)){
throw new BizException("请求faceId失败");
}
String faceId = object.getJSONObject("result").getString("faceId");
return faceId;
}
public FaceIdResult getFaceIdResult(String appId,String version,String nonce,String orderNo,String sign,String getFile){
HashMap<String, Object> map = new HashMap<>();
map.put("app_id",appId);
map.put("version","1.0.0");
map.put("nonce",nonce);
map.put("sign",sign);
map.put("order_no",orderNo);
map.put("get_file",getFile);
String resp = HttpUtil.get("https://miniprogram-kyc.tencentcloudapi.com/api/server/sync", map);
log.info("身份认证结果{}",resp);
JSONObject object = JSONObject.parseObject(resp);
String code = object.getString("code");
if (!SUCCESS.equals(code)){
log.info("请求身份认证查询失败 code:"+code);
FaceIdResult faceIdResult = new FaceIdResult();
faceIdResult.setResult(false);
return faceIdResult;
}
FaceIdResult result = object.getObject("result", FaceIdResult.class);
result.setResult(true);
return result;
}
public static String getRandomNumByLength(int length) {
String base = "ABCDEFGHIJKMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for ( int i = 0; i < length; i++ ){
int number = random.nextInt( base.length() );
sb.append( base.charAt( number ) );
}
return sb.toString();
}
public String getRandomNumByLength(Integer randomNumLength, String type){
String rNum = String.valueOf(System.currentTimeMillis());
Integer typeLength = type.length();
int i = randomNumLength - rNum.length()-typeLength;
String nonceStr = type+rNum+ getRandomNumByLength(i);
return nonceStr;
}
}
Controller (服务端接口)
@PostMapping("face-init-sdk")
public R<FaceIdSdkInitResp> faceInitSdk(@RequestBody @Valid FaceInitReq faceInitReq){
return faceIdAuthService.faceInitSdk(faceInitReq);
}
service (接口实现)
@Override
@Transactional(rollbackFor = Exception.class)
public R<FaceIdSdkInitResp> faceInitSdk(FaceInitReq req) {
String order = client.getRandomNumByLength(16, "ORDER");
//记录用户认证的信息
try {
//获取缓存中的信息
String token = redisService.getString(RedisKeyUtil.getFaceAccessToken());
String signTicketKey = RedisKeyUtil.getSignTicket(token);
String signTicket = redisService.getString(signTicketKey);
if (Objects.isNull(signTicket)){
//重新获取,并存入缓存
token = client.getAccessToken();
signTicket = client.getSignTicket(token);
redisService.setString(RedisKeyUtil.getFaceAccessToken(), token, RedisConstant.ACCESS_TOKEN_TIME_OUT);
String ticketKey = RedisKeyUtil.getSignTicket(token);
redisService.setString(ticketKey,signTicket);
}
String nonce = client.getRandomNumByLength(32, "NONCE");
String nonceTicket = client.getApiTicket(token, String.valueOf(req.getUserId()));
String sign = client.getSign(faceIdInfo.getWBappid(), String.valueOf(req.getUserId()), "1.0.0", signTicket,nonce);
String apiSign = client.getSign(faceIdInfo.getWBappid(), String.valueOf(req.getUserId()), "1.0.0", nonceTicket, nonce);
FaceIdReq faceIdReq = new FaceIdReq();
faceIdReq.setWebankAppId(faceIdInfo.getWBappid());
faceIdReq.setOrderNo(order);
faceIdReq.setName(req.getUserName());
faceIdReq.setIdNo(req.getIdCard());
faceIdReq.setUserId(String.valueOf(req.getUserId()));
faceIdReq.setSign(sign);
faceIdReq.setNonce(nonce);
String faceId = client.getFaceId(faceIdReq);
FaceIdSdkInitResp resp = new FaceIdSdkInitResp();
resp.setFaceId(faceId);
resp.setAgreementNo(order);
resp.setOpenApiAppId(faceIdInfo.getWBappid());
resp.setOpenApiNonce(nonce);
resp.setOpenApiUserId(String.valueOf(req.getUserId()));
resp.setOpenApiSign(apiSign);
resp.setKeyLicence(faceIdInfo.getKeyLicense());
return R.ok(resp);
} catch (Exception e) {
throw new BizException(e.getMessage());
}
}
请求参数
@Data
public class FaceInitReq extends BaseRequest {
@NotNull
private String userName;
@NotNull
private String IdCard;
@NotNull
private Long userId;
}
服务端获取人脸结果
String token = redisService.getString(RedisKeyUtil.getFaceAccessToken());
String signTicketKey = RedisKeyUtil.getSignTicket(token);
String signTicket = redisService.getString(signTicketKey);
if (Objects.isNull(signTicket)){
//重新获取,并存入缓存
token = client.getAccessToken();
signTicket = client.getSignTicket(token);
redisService.setString(RedisKeyUtil.getFaceAccessToken(), token, RedisConstant.ACCESS_TOKEN_TIME_OUT);
String ticketKey = RedisKeyUtil.getSignTicket(token);
redisService.setString(ticketKey,signTicket);
}
String nonce = client.getRandomNumByLength(32, "NONCE");
String sign = client.getSign(faceIdInfo.getWBappid(), faceIdResultReq.getOrderId(), "1.0.0", signTicket,nonce);
FaceIdResult faceIdResult = client.getFaceIdResult(faceIdInfo.getWBappid(), "1.0.0", nonce,faceIdResultReq.getOrderId(), sign, "0");
以上就是接入腾讯人脸核身的大概流程