前言
大致一个流程是,用户输入身份证、姓名,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;
}
}
}