腾讯云人脸核身服务端实现(一)

前言: 服务端需要将faceId以及nonceSign 以及其他数据传递给移动端,移动端根据返回的参数进行拉起,以下代码只是为了跑通流程,请忽略格式,规范

  1. 腾讯云后台申请人脸核身功能权限,地址:console.cloud.tencent.com/faceid/acce...
  2. 申请成功后图片样式

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>
  1. 获取yml 中的参数

    java 复制代码
     package 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();
    }
    
    }
  2. 签名生成工具

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();
    }
}
  1. 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();
    }

}
  1. 获取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;
}
  1. 获取 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;
    }
  1. 根据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());


    }
  1. 获取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;
    }


}
  1. 获取所有信息返回给前端,前端将参数传递给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;
    }
相关推荐
Chandler2415 分钟前
Go:接口
开发语言·后端·golang
ErizJ18 分钟前
Golang|Channel 相关用法理解
开发语言·后端·golang
automan0218 分钟前
golang 在windows 系统的交叉编译
开发语言·后端·golang
Pandaconda18 分钟前
【新人系列】Golang 入门(十三):结构体 - 下
后端·golang·go·方法·结构体·后端开发·值传递
我是谁的程序员26 分钟前
Flutter iOS真机调试报错弹窗:不受信任的开发者
后端
蓝宝石Kaze27 分钟前
使用 Viper 读取配置文件
后端
aiopencode29 分钟前
Flutter 开发指南:安卓真机、虚拟机调试及 VS Code 开发环境搭建
后端
开心猴爷33 分钟前
M1搭建flutter环境+真机调试demo
后端
沐道PHP34 分钟前
Go Gin框架安装记录
后端
技术宝哥1 小时前
解决 Spring Boot 启动报错:数据源配置引发的启动失败
spring boot·后端·mybatis