【聚美智数】说说人脸识别中的活体检测吧

前言

大致一个流程是,用户输入身份证、姓名,app端使用第三方sdk进行活体检测(点头、张嘴之类的动作)由app端设置,然后截图转递给后端,后端根据图片唤起第三方请求,对比人脸。该第三方不保存用户上传的图片等数据。本文章是结合官网demo自己整理, 官网

目录结构

在这里插入图片描述

文件配置

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.12</version>
</dependency>

<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.11</version>
</dependency>

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
</dependency>
# 商户后台获取
chat:
  face:
    appId: 
    appSecret: 

代码

// 第三方请求api,由他们提供
public class JumConstants {
	// 人脸
    public static final String FACE_SERVER_URL = "";
    // 身份证
    public static final String IDCARD_SERVER_URL = "";

}
@Data
public class IdcardInfo implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 身份证号
     */
    private String number;

    /**
     *地址
     */
    private String address;

    /**
     *出⽣月
     */
    private String month;

    /**
     *民族
     */
    private String nation;

    /**
     *出⽣年
     */
    private String year;

    /**
     *性别
     */
    private String sex;
    /**
     *姓名
     */
    private String name;
    /**
     *出⽣日
     */
    private String day;
    /**
     *签发机关
     */
    private String authority;
    /**
     *身份证有效期
     */
    private String timelimit;
}
@Data
public abstract class ResultBodyBO implements Serializable {
    private static final long serialVersionUID = 1L;
    private int code;

    private String msg;

    private String taskNo;

    private boolean success;

    private boolean charge;
}
@Data
public class ResultFaceBodyBO extends ResultBodyBO implements Serializable {
    private static final long serialVersionUID = 1L;
    private ResultFaceDataBO data;
}
public class ResultFaceDataBO implements Serializable {
    private static final long serialVersionUID = 1L;
    /**姓名*/
    private String order_no;

    /**身份证号*/
    private String score;

    /**核验结果状态码,1 一致;2 不一致;3 无记录*/
    private String msg;

    /**核验结果状态描述*/
    private String incorrect;

    /**性别*/
    private String sex;

    /**生日*/
    private String birthday;

    /**身份证地址*/
    private String address;
}
public class ResultIDCardBodyBO extends ResultBodyBO implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * 返回具体结果,object类型,详见data返回字段描述
     */
    private ResultIDCardDataBO data;

}
public class ResultIDCardDataBO implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * 订单号
     */
    private String order_no;

    /**
     * 扫描结果,0 - 扫描成功;1 - 扫描失败
     */
    private int result;

    /**
     * 正面或版面,front - 身份证正面(⼈像面), back - 身份证反面(国徽⾯)
     */
    private String side;

    /**
     * 旋转角度。返回旋转的角度值,可能的值为 cw90,cw180,cw270,表明顺时针旋转的角度。
     * 如果没有发生旋转,则不返回此字段
     */
    private String rotate;

    /**
     * 扫描结果信息,分正面和反面,详见info字段说明
     */
    private IdcardInfo info;
}
  • FaceProperties
// 读取yml配置文件信息
@Data
@ConfigurationProperties(prefix = "chat.face")
public class FaceProperties {
    private String appId;
    private String appSecret;
}
  • FaceTemplate
// 发送第三请求及业务逻辑处理
@Slf4j
public class FaceTemplate {

    private FaceProperties properties;
    public FaceTemplate(FaceProperties properties) {
        this.properties = properties;
    }

    private String baseMethod(Map<String, Object> params, String postUrl, String imgUrl) {
        String timestamp = System.currentTimeMillis() + "";
        String sign = DigestUtils.sha256Hex(properties.getAppId() + properties.getAppSecret() + timestamp);
        params.put("appId", properties.getAppId());
        params.put("timestamp", timestamp);
        params.put("sign", sign);
        params.put("url", imgUrl);
        return HttpUtils.postForm(postUrl, null, params);
    }

    /**
     * 人脸识别
     * @param idcard 身份证号码
     * @param username 身份证姓名
     * @param faceImageUrl 人脸图片地址
     * @return
     */
    public ResultFaceBodyBO sendJMFaceRecognition(String idcard, String username, String faceImageUrl) {
        try {
            Map<String, Object> params = new HashMap<>(2);
            params.put("idcard", idcard);
            params.put("name", username);
            JSONObject jsonObject = JSONUtil.parseObj(baseMethod(params, JumConstants.FACE_SERVER_URL, faceImageUrl));
            return jsonObject.toBean(ResultFaceBodyBO.class);
        } catch (Exception e) {
            log.error("人脸识别失败:{},", e.getMessage());
            throw  new BusinessException(e);
        }
    }

    /**
     * 身份证识别
     * @param idcardImgUrl 图片地址
     * @return
     */
    public ResultIDCardBodyBO sendJMIDCardIdentification(String idcardImgUrl) {
        try {
            JSONObject jsonObject = JSONUtil.parseObj(baseMethod(new HashMap<>(4), JumConstants.IDCARD_SERVER_URL, idcardImgUrl));
            return jsonObject.toBean(ResultIDCardBodyBO.class);
        } catch (Exception e) {
            log.error("身份证识别失败:{}", e.getMessage());
            throw new BusinessException(e);
        }
    }

}
  • BusinessException
/**
 * 通用业务异常类
 */
public class BusinessException extends RuntimeException {
    private static final long serialVersionUID = 1L;
    public BusinessException() {
        super();
    }

    public BusinessException(String message) {
        super(message);
    }

    public BusinessException(String message, Throwable cause) {
        super(message, cause);
    }

    public BusinessException(Throwable cause) {
        super(cause);
    }

    protected BusinessException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}
  • HttpUtils
// http 工具
public class HttpUtils {

    private static final String DEFAULT_CHARSET = "UTF-8";

    private static final Logger LOGGER = LoggerFactory.getLogger(HttpUtils.class);

    private static PoolingHttpClientConnectionManager connectionManager;

    private static HttpClient httpClient;

    static {
        try {
            SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, new TrustSelfSignedStrategy()).build();
            SSLConnectionSocketFactory sslSf = new SSLConnectionSocketFactory(sslContext,
                    SSLConnectionSocketFactory.getDefaultHostnameVerifier());
            Registry<ConnectionSocketFactory> socketFactoryRegistry =
                    RegistryBuilder.<ConnectionSocketFactory>create().register("https", sslSf).register("http",
                            PlainConnectionSocketFactory.getSocketFactory()).build();
            connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
            connectionManager.setMaxTotal(100);
            connectionManager.setDefaultMaxPerRoute(50);
            httpClient = getSSLHttpClient();
        } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {
            LOGGER.error("HttpUtils class initialize error|message={}", e.getMessage(), e);
        }
    }

    private HttpUtils() {
    }

    public static CloseableHttpClient getHttpClient() {
        return HttpClients.custom().setConnectionManager(connectionManager)
                .setRetryHandler((IOException exception, int executionCount, HttpContext context) -> {
                    if (executionCount >= 3) {
                        return false;
                    }
                    return true;
                }).build();
    }

    public static CloseableHttpClient getSSLHttpClient() {
        // 使用TrustSelfSignedStrategy容许Self Signed的证书
        SSLContext sslContext = null;
        try {
            sslContext = SSLContextBuilder.create().loadTrustMaterial(new TrustSelfSignedStrategy()).build();
        } catch (Exception e) {
            LOGGER.warn("method getSSLHttpClient error,message={}", e.getMessage(), e);
        }
        // 不做服务器名验证,这是可选的, 如果需要对服务器名做验证,可以去掉该配置
        HostnameVerifier allowAllHosts = new NoopHostnameVerifier();
        // 使用SSLContext创建SSL Socket Factory
        SSLConnectionSocketFactory connectionFactory = new SSLConnectionSocketFactory(sslContext, allowAllHosts);
        // 使用HttpClient Factory创建HttpClient
        return HttpClients.custom().setSSLSocketFactory(connectionFactory).build();
    }

    /**
     * get 请求
     */
    public static String get(String uri, Map<String, String> headers, Map<String, Object> data, int timeOut) {
        HttpClient client = getHttpClient();
        List<NameValuePair> params = new ArrayList<>();
        if (data != null) {
            for (Map.Entry entry : data.entrySet()) {
                if (null != entry.getValue()) {
                    params.add(new BasicNameValuePair(entry.getKey() + "", entry.getValue() + ""));
                }
            }
        }
        try {
            String urlFull = null;
            if (params.isEmpty()) {
                urlFull = uri;
            } else {
                String paramsStr = EntityUtils.toString(new UrlEncodedFormEntity(params, DEFAULT_CHARSET));
                urlFull = uri + "?" + paramsStr;
            }

            HttpGet get = new HttpGet(urlFull);

            if (timeOut > 0) {
                RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(3000).setSocketTimeout(timeOut).build();
                get.setConfig(requestConfig);
            }

            if (headers != null) {
                for (Map.Entry<String, String> entry : headers.entrySet()) {
                    get.addHeader(entry.getKey(), entry.getValue());
                }
            }
            HttpResponse response = client.execute(get);
            HttpEntity entity = response.getEntity();
            return EntityUtils.toString(entity, "UTF-8").trim();
        } catch (Exception e) {
            LOGGER.error("method get error|message={}", e.getMessage(), e);
            return null;
        }
    }

    public static String get(String uri, Map<String, String> headers, Map<String, Object> data) {
        return get(uri, headers, data, 0);
    }

    /**
     * post 发送表单请求
     */
    public static String postForm(String uri, Map<String, String> headers, Map<String, Object> data, int timeOut) {
        HttpClient client = getHttpClient();
        HttpPost post = new HttpPost(uri);

        if (timeOut > 0) {
            RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(timeOut).build();
            post.setConfig(requestConfig);
        }

        List<NameValuePair> form = new ArrayList<>();
        for (Map.Entry entry : data.entrySet()) {
            if (null != entry.getValue()) {
                form.add(new BasicNameValuePair(entry.getKey() + "", entry.getValue() + ""));
            }
        }
        if (headers != null) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                post.addHeader(entry.getKey(), entry.getValue());
            }
        }
        try {
            post.setEntity(new UrlEncodedFormEntity(form, DEFAULT_CHARSET));
            HttpResponse response = client.execute(post);
            HttpEntity entity = response.getEntity();
            return EntityUtils.toString(entity, "UTF-8").trim();
        } catch (Exception e) {
            LOGGER.error("method postForm error|message={}", e.getMessage(), e);
            return null;
        }
    }

    public static String postForm(String uri, Map<String, String> headers, Map<String, Object> data) {
        return postForm(uri, headers, data, 0);
    }

    /**
     * post 发送json报文请求
     * content 请求参数json字符串
     */
    public static String postJson(String uri, Map<String, String> headers, String content) {
        HttpClient client = getHttpClient();
        HttpPost post = new HttpPost(uri);
        post.setEntity(new StringEntity(content, DEFAULT_CHARSET));
        if (headers != null) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                post.addHeader(entry.getKey(), entry.getValue());
            }
        }
        return postCommon(client, post);
    }

    /**
     * post 发送json报文请求
     *
     * @param uri
     * @param headers
     * @param content
     * @param timeOut 单位毫秒
     * @return
     */
    public static String postJson(String uri, Map<String, String> headers, String content, Integer timeOut) {
        CloseableHttpClient client = getHttpClient();
        HttpPost post = new HttpPost(uri);
        RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(timeOut).build();
        post.setConfig(requestConfig);
        post.setEntity(new StringEntity(content, DEFAULT_CHARSET));
        if (headers != null) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                post.addHeader(entry.getKey(), entry.getValue());
            }
        }
        return postCommon(client, post);
    }

    /**
     * TLS/SSL 加密方式发送json报文
     *
     * @param uri
     * @param headers
     * @param content
     * @return
     */
    public static String postSSLData(String uri, Map<String, String> headers, String content) {
        HttpPost post = new HttpPost(uri);
        post.setEntity(new StringEntity(content, DEFAULT_CHARSET));
        if (headers != null) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                post.addHeader(entry.getKey(), entry.getValue());
            }
        }
        return postCommon(httpClient, post);
    }

    public static CloseableHttpResponse getResponse(String uri, Map<String, String> headers, Map<String, Object> data) throws Exception {
        CloseableHttpClient client = getHttpClient();
        List<NameValuePair> params = new ArrayList<>();
        if (data != null) {
            for (Map.Entry entry : data.entrySet()) {
                if (null != entry.getValue()) {
                    params.add(new BasicNameValuePair(entry.getKey() + "", entry.getValue() + ""));
                }
            }
        }
        String str = EntityUtils.toString(new UrlEncodedFormEntity(params, DEFAULT_CHARSET));
        HttpGet get = new HttpGet(uri + "?" + str);
        if (headers != null) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                get.addHeader(entry.getKey(), entry.getValue());
            }
        }
        return client.execute(get);
    }

    private static String postCommon(HttpClient client, HttpPost post) {
        try {
            HttpResponse response = client.execute(post);
            HttpEntity entity = response.getEntity();
            return EntityUtils.toString(entity, "UTF-8").trim();
        } catch (Exception e) {
            LOGGER.error("method postData error|message={}", e.getMessage(), e);
            return null;
        }
    }


}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值