前言: 服务端需要将faceId以及nonceSign 以及其他数据传递给移动端,移动端根据返回的参数进行拉起,以下代码只是为了跑通流程,请忽略格式,规范
- 腾讯云后台申请人脸核身功能权限,地址:console.cloud.tencent.com/faceid/acce...
- 申请成功后图片样式
3. yml 定义秘钥和id

pom 依赖:SignUtil 中需要此依赖
xml
<!-- https://mvnrepository.com/artifact/dev.mccue/guava-hash -->
<dependency>
<groupId>dev.mccue</groupId>
<artifactId>guava-hash</artifactId>
<version>33.4.0</version>
</dependency>
-
获取yml 中的参数
javapackage com.alan.config; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @ConfigurationProperties(prefix = "tencent.cloud") public class TencentCloudConfig { private String wbAppId; // 人脸核身appId private String wbAppIdTest; // 人脸核身appId private String secret; private String secretTest; public String getWbAppId() { return wbAppId; } public void setWbAppId(String wbAppId) { this.wbAppId = wbAppId; } public String getSecret() { return secret; } public void setSecret(String secret) { this.secret = secret; } public String getWbAppIdTest() { return wbAppIdTest; } public void setWbAppIdTest(String wbAppIdTest) { this.wbAppIdTest = wbAppIdTest; } public String getSecretTest() { return secretTest; } public void setSecretTest(String secretTest) { this.secretTest = secretTest; } public String realAppId(){ return getWbAppIdTest(); } public String realSecret() { return getSecretTest(); } }
-
签名生成工具
java
package com.alan.utils;
import dev.mccue.guava.hash.Hashing;
import org.apache.commons.codec.Charsets;
import java.util.Collections;
import java.util.List;
public class SignUtils {
public static String sign(List<String> values, String ticket) { //values传ticket外的其他参数
if (values == null) {
throw new NullPointerException("values is null");
}
values.removeAll(Collections.singleton(null));// remove 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();
}
}
- 32位随机数生成工具
java
package com.alan.utils;
import java.security.SecureRandom;
public class RandomStringGenerator {
private static final String ALPHA_NUMERIC_STRING = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
private static final SecureRandom random = new SecureRandom();
public static String generate32Chars() {
StringBuilder builder = new StringBuilder(32);
for (int i = 0; i < 32; i++) {
int index = random.nextInt(ALPHA_NUMERIC_STRING.length());
builder.append(ALPHA_NUMERIC_STRING.charAt(index));
}
return builder.toString();
}
}
- 获取AccessToken,获取 Sign Ticket以及Nonce Ticket 都需要这个值,官方推荐保存此值,每20分钟刷新一次,下面只做获取逻辑,并没有进行保存,而是每次都获取新的值
csharp
/**
* https://kyc1.qcloud.com/api/oauth2/access_token
* 注意事项
* 所有场景默认采用 UTF-8 编码。
* Access Token 必须缓存在磁盘,并定时刷新,且不能并发刷新,建议每20分钟请求新的 Access Token,获取之后立即使用最新的 Access Token。旧的只有一分钟的并存期。
* 如果未按照上述做定时刷新,可能导致鉴权不通过,影响人脸服务正常调用。
* 每次用户登录时必须重新获取 NONCE ticket。
* 请求
* 请求 URL:https://kyc1.qcloud.com/api/oauth2/access_token
* 请求方法:GET
*/
public String getAccessToken() {
RestTemplate restTemplate = new RestTemplate();
Map<String, String> params = new HashMap<>();
params.put("appId", tencentCloudConfig.realAppId());
params.put("secret", tencentCloudConfig.realSecret());
params.put("grant_type", "client_credential");//client_credential
params.put("version", "1.0.0");
// 拼接查询参数
StringBuilder queryString = new StringBuilder();
for (Map.Entry<String, String> entry : params.entrySet()) {
if (!queryString.isEmpty()) {
queryString.append("&");
}
queryString.append(entry.getKey()).append("=").append(entry.getValue());
}
String url = "https://kyc1.qcloud.com/api/oauth2/access_token" + "?" + queryString;
TencentAccessTokenResponse tencentApiTicketResponse = restTemplate.getForObject(url, TencentAccessTokenResponse.class);
if (tencentApiTicketResponse != null) {
if (tencentApiTicketResponse.getCode().equals("0")) {
return tencentApiTicketResponse.getAccess_token();
}
} else {
return null;
}
return null;
}
- 获取 Ticket方法
typescript
/**
* 注意事项
* 前置条件:请合作方确保 Access Token 已经正常获取,获取方式请参见 Access Token 获取。
* 用途:NONCE ticket 是合作方前端包含 App 和 H5 等生成签名鉴权参数之一,启动 H5 或 SDK 人脸核身。
* API ticket 的 NONCE 类型,其有效期为120秒,且一次性有效,即每次启动 SDK 刷脸都要重新请求 NONCE ticket。
* 请求
* 请求 URL:https://kyc1.qcloud.com/api/oauth2/api_ticket
* 请求方法:GET
* type 为NONCE 和 SIGN,NONCE: 最后返回给移动端的类型,SIGN 用户获取faceId 的一个步骤
*/
public TencentApiTicketResponse getApiTicket(String accessToken, String userId, String type) {
RestTemplate restTemplate = new RestTemplate();
Map<String, String> params = new HashMap<>();
params.put("appId", tencentCloudConfig.realAppId()); //
params.put("access_token", accessToken); //
params.put("type", type); //
params.put("version", "1.0.0"); //
if (type.equals("NONCE")) {
params.put("user_id", userId);
}
// ResponseEntity<TencentApiTicketResponse> responseEntity = restTemplate.getForEntity("https://kyc1.qcloud.com/api/oauth2/api_ticket", TencentApiTicketResponse.class, params);
// TencentApiTicketResponse tencentApiTicketResponse = responseEntity.getBody();
// 拼接查询参数
StringBuilder queryString = new StringBuilder();
for (Map.Entry<String, String> entry : params.entrySet()) {
if (!queryString.isEmpty()) {
queryString.append("&");
}
queryString.append(entry.getKey()).append("=").append(entry.getValue());
}
String url = "https://kyc1.qcloud.com/api/oauth2/api_ticket" + "?" + queryString;
TencentApiTicketResponse tencentApiTicketResponse = restTemplate.getForObject(url, TencentApiTicketResponse.class);
if (tencentApiTicketResponse != null) {
if (tencentApiTicketResponse.getCode().equals("0")) {
return tencentApiTicketResponse;
}
} else {
return null;
}
return null;
}
- 根据type的类型,生成对应的sign ,NONCE 类型的sign 直接返回给移动端,SIGN类型的sign 获取faceId的时候需要此参数,只是为了获取faceId
typescript
/**
* 获取 NONCE 类型签名,最终返回给移动端
*
* @param nonce
* @param userId
* @param orderNum
* @param version
* @param type NONCE或者 SIGN
* @return
*/
public String getSign(String nonce, String userId,
String orderNum, String version, String type, String accessToken) {
// 获取NONCE ticket
TencentApiTicketResponse responseSign = getApiTicket(accessToken, userId, type);
TencentApiTicketResponse.TicketModel ticket = responseSign.getTickets().get(0);
ArrayList<String> needSortedData = new ArrayList<>();
needSortedData.add(tencentCloudConfig.realAppId());//appId
needSortedData.add(userId);//userId
needSortedData.add(version);//version
// needSortedData.add(ticket.getValue());//ticket
needSortedData.add(nonce);//nonce
return SignUtils.sign(needSortedData, ticket.getValue());
}
- 获取faceId 方法
typescript
// 合作方后台上传身份信息
// 请求
// 请求 URL:https://kyc1.qcloud.com/api/server/getfaceid?orderNo=xxx
// 注意
// 为方便查询耗时,该请求 url 后面请拼接 orderNo 订单号参数。
// 请求方法:POST
// 报文格式:Content-Type: application/json
public FaceIdResponse getFaceId(String userId, String sign, String nonce, String orderNum) {
RestTemplate restTemplate = new RestTemplate();
Map<String, String> params = new HashMap<>();
params.put("appId", tencentCloudConfig.realAppId()); //
params.put("userId", userId); //
params.put("name", "你的名字"); //使用权威库需要
params.put("idNo", "你的身份证号"); //使用权威库需要
params.put("version", "1.0.0"); //
params.put("sign", sign); //
params.put("nonce", nonce);// 32位随机数
params.put("orderNo", orderNum);// 订单号
// 1. 设置请求头(Content-Type: application/json)
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
// 3. 封装请求实体(包含 headers 和 body)
HttpEntity<Map<String, String>> requestEntity = new HttpEntity<>(params, headers);
String url = "https://kyc1.qcloud.com/api/server/getfaceid" + "?orderNo=" + orderNum;
// String url = "https://kyc1.qcloud.com/api/server/getfaceid";
// FaceIdResponse faceIdResponse = restTemplate.postForObject(url, params, FaceIdResponse.class);
ResponseEntity<FaceIdResponse> response = restTemplate.postForEntity(
url,
requestEntity,
FaceIdResponse.class
);
FaceIdResponse faceIdResponse = response.getBody();
if (faceIdResponse != null) {
if (faceIdResponse.getCode().equals("0")) {
return faceIdResponse;
}
} else {
return null;
}
return null;
}
}
- 获取所有信息返回给前端,前端将参数传递给sdk,拉起刷脸
ini
@GetMapping("/get-sign")
public Map<String, Object> getSign() {
String nonce = RandomStringGenerator.generate32Chars();
System.out.println("64-bit nonce: " + nonce);
String userId = "userId000000000001";
String orderNum = "orderNum0000000001";
String version = "1.0.0";
String appId = tencentCloudConfig.realAppId();
Map<String, Object> map = new HashMap<>();
String accessToken = tencentCloudService.getAccessToken();
// 获取nonceSign 返回给前端
String nonceSign = tencentCloudService.getSign(nonce, userId, orderNum, version, "NONCE", accessToken);
// 获取faceId
String signSign = tencentCloudService.getSign(nonce, userId, orderNum, version, "SIGN", accessToken);
// String faceNonce = RandomStringGenerator.generate32Chars();
String faceId = tencentCloudService.getFaceId(userId, signSign, nonce, orderNum).getResult().getFaceId();
map.put("faceId", faceId);
map.put("orderNo", orderNum);
map.put("appId", appId);
map.put("version", version);
map.put("nonce", nonce);
map.put("userId", userId);
map.put("sign", nonceSign);
return map;
}