微信人脸核身后台接口开发记录
2022-04-29 本文已影响0人
一介书生独醉江湖
本文只提供后台接口部分,不提供小程序部分
首先需要登录小程序账号在后台、接入人脸核身
微信官方文档:
https://developers.weixin.qq.com/community/business/doc/000442d352c1202bd498ecb105c00d
# 二、准备接入 (请在小程序发布后,再提交人脸核身接口申请)
# 参考上面文档中的准备接入部分,按照步骤接入即可,这里略过;
application.yml
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.60</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.1.19</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>1.9.6</version>
</dependency>
# JsApi是核心类,上面的依赖是为了支持其他几个工具类的
package com.example.favoritecode.jsapi;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import javax.annotation.Nonnull;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
/**
* @Author ds
* @Date 2021-09-25
*/
public class JsApi {
private static JsApi jsApi = new JsApi();
public JsApi(){
}
public static JsApi getInstance(){
return jsApi;
}
private static final String ACCESS_TOKEN_GRANT_TYPE = "client_credential";
private static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token";
private static final String GET_USER_ID_KEY_URL = "https://api.weixin.qq.com/cityservice/face/identify/getuseridkey";
private static final String GET_INFO = "https://api.weixin.qq.com/cityservice/face/identify/getinfo";
/**
* 获取user_id_key
* @return
*/
public static UserIdKeyVo getUserIdKey(String appId, String appSecret, String name , String idCard){
String accessToken = JsApi.getAccessToken(appId, appSecret);
if(accessToken == null) {
System.out.println("获取user_id_key 时,access_token 为 null");
return null;
}
Map<String,Object> params = new HashMap<>(8);
params.put("name",name);
params.put("id_card_number",idCard);
JSONObject paramJson = new JSONObject(params);
String result = HttpUtil.post(GET_USER_ID_KEY_URL + "?access_token=" + accessToken, paramJson.toJSONString());
JSONObject jsonObj = JSONObject.parseObject(result);
if(jsonObj == null){
return null;
}
UserIdKeyVo userIdKeyVo = JSONObject.parseObject(jsonObj.toString(), UserIdKeyVo.class);
System.out.println("userIdKeyVo : " + userIdKeyVo);
return userIdKeyVo;
}
public static GetWxFaceInfoVo getInfo(String appId, String appSecret, String verifyResult){
String accessToken = JsApi.getAccessToken(appId, appSecret);
if(accessToken == null) {
System.out.println("获取getInfo 时,access_token 为 null");
return null;
}
Map<String,Object> params = new HashMap<>(8);
params.put("verify_result",verifyResult);
JSONObject paramJson = new JSONObject(params);
String result = HttpUtil.post(GET_INFO + "?access_token=" + accessToken, paramJson.toJSONString());
JSONObject jsonObj = JSONObject.parseObject(result);
if(jsonObj == null){
return null;
}
GetWxFaceInfoVo getWxFaceInfoVo = JSONObject.parseObject(jsonObj.toString(), GetWxFaceInfoVo.class);
System.out.println("getWxFaceInfoVo : " + getWxFaceInfoVo);
return getWxFaceInfoVo;
}
/**
* 获取公众号的全局唯一接口调用凭据
*/
public static String getAccessToken(String appId, String appSecret){
String getAccessTokenParam = null;
String accessToken = null;
String wechatResult = null;
try {
// 拼接凭证参数
getAccessTokenParam = "appid=" + appId + "&secret=" + appSecret + "&grant_type=" + ACCESS_TOKEN_GRANT_TYPE;
// 获取凭证
HttpURLReturnBean httpURLReturnBean = HttpURLUtils.sendGet(ACCESS_TOKEN_URL, getAccessTokenParam, null, null, StandardCharsets.UTF_8);
if (httpURLReturnBean.getHttpStatusCode() != 200) {
System.out.println("get wechat access token error. response " + httpURLReturnBean);
}
wechatResult = httpURLReturnBean.getResult();
} catch (Exception e) {
System.out.println("get wechat access token exception. " + e.getMessage());
}
if (StringUtils.isBlank(wechatResult)) {
System.out.println("get wechat access token exception, not fund response result, requeset params " + getAccessTokenParam);
}
// 解析微信返回消息
Map<String, String> acessTokenResponseData = acesstoken(wechatResult);
if (acessTokenResponseData.get("accessToken") == null) {
return null;
}
accessToken = acessTokenResponseData.get("accessToken");
System.out.println("获取公众号的全局唯一接口调用凭据 accessToken : " + accessToken);
return accessToken;
}
public static Map<String, String> acesstoken(@Nonnull String jsonStr) {
Map<String, String> map = new HashMap<>();
JSONObject jsonObj = null;
try {
jsonObj = JSONObject.parseObject(jsonStr);
} catch (Exception e) {
System.out.println("parsing wechat jscode2session result exception. " + e.getMessage());
}
if (jsonObj == null) {
// 失败
map.put("accessToken", null);
map.put("expiresIn", "");
return map;
}
Object accessToken = jsonObj.get("access_token");
if (accessToken == null) {
// 失败
map.put("accessToken", null);
map.put("expiresIn", "");
return map;
}
// 成功
map.put("accessToken", accessToken.toString());
map.put("expiresIn", jsonObj.getString("expires_in"));
return map;
}
}
package com.example.favoritecode.jsapi;
import lombok.Data;
import java.util.Date;
/**
* @Author ds
* @Date 2021-09-25
*/
@Data
public class GetWxFaceInfoVo {
/**
* 注:errcode和identify_ret同时为0,代表本次认证成功。
*/
// 错误码, 0表示本次api调用成功
private int errcode;
// 本次api调用的错误信息
private String errmsg;
// 人脸核身最终认证结果
private int identify_ret;
// 认证时间
private Date identify_time;
// 用户读的数字(如是读数字)
private String validate_data;
// 用户openid
private String openid;
// 用于后台交户表示用户姓名、身份证的凭证
private String user_id_key;
// 认证结束时间
private Date finish_time;
// 身份证号的md5(最后一位X为大写)
private String id_card_number_md5;
// 姓名MD5
private String name_utf8_md5;
}
package com.example.favoritecode.jsapi;
import java.io.Serializable;
/**
* HttpURLUtils返回值</br>
*
* @author ds
* @date 2020-03-18 17:00:22
*/
public final class HttpURLReturnBean implements Serializable {
/**
* @Fields serialVersionUID : serialVersionUID
*/
private static final long serialVersionUID = 977562102461091891L;
private int httpStatusCode;
private String result;
/**
* httpStatusCode.
*
* @return the httpStatusCode
*/
public int getHttpStatusCode() {
return httpStatusCode;
}
/**
* httpStatusCode.
*
* @param httpStatusCode the httpStatusCode to set
*/
public void setHttpStatusCode(int httpStatusCode) {
this.httpStatusCode = httpStatusCode;
}
/**
* result.
*
* @return the result
*/
public String getResult() {
return result;
}
/**
* result.
*
* @param result the result to set
*/
public void setResult(String result) {
this.result = result;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("HttpURLReturnBean{");
sb.append("httpStatusCode=").append(httpStatusCode);
sb.append(", result='").append(result).append('\'');
sb.append('}');
return sb.toString();
}
}
package com.example.favoritecode.jsapi;
import com.alibaba.fastjson.util.IOUtils;
import com.sun.xml.internal.ws.util.UtilException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.Charset;
/**
* Http请求工具类</br>
*
* @author ds
* @date 2020-03-18 17:00:22
*/
public final class HttpURLUtils {
private static final Logger logger = LoggerFactory.getLogger(HttpURLUtils.class);
/**
* GET请求</br>
*
* @param strURL URL字符串
* @param param "&"链接的键值对
* @param token 授权token
* @param contentType contentType
* @param charset StandardCharsets.xxx
* @return HttpURLReturnBean HttpURLReturnBean
*/
public static HttpURLReturnBean sendGet(@Nonnull String strURL, @Nullable String param,
String token, String contentType, Charset charset) throws UtilException {
logger.info("sendGet URL [{}], params [{}], token [{}], contentType[{}]", strURL, param, token, contentType);
StringBuilder result = new StringBuilder();
HttpURLReturnBean httpURLStatusBean = new HttpURLReturnBean();
PrintWriter out = null;
BufferedReader in = null;
HttpURLConnection conn;
try {
URL realUrl = new URL(strURL);
conn = (HttpURLConnection) realUrl.openConnection();
conn.setRequestMethod("GET");
if (StringUtils.isNotBlank(token)) {
conn.addRequestProperty("Authorization", "Bearer " + token);
}
if (StringUtils.isNotBlank(contentType)) {
conn.setRequestProperty("Content-Type", contentType);
}
conn.setDoInput(true);
if (StringUtils.isNotBlank(param)) {
conn.setDoOutput(true);
out = new PrintWriter(conn.getOutputStream());
out.print(param);
out.flush();
}
httpURLStatusBean.setHttpStatusCode(conn.getResponseCode());
try {
in = new BufferedReader(new InputStreamReader(conn.getInputStream(), charset));
} catch (Exception e) {
in = new BufferedReader(new InputStreamReader(conn.getErrorStream(), charset));
}
String line;
while ((line = in.readLine()) != null) {
result.append(line);
}
} catch (Exception e) {
System.err.println("\n****************************** HttpURLUtils.sendGet ******************************");
System.err.println("** strURL:\t" + strURL);
System.err.println("** param:\t" + param);
System.err.println("** token:\t" + token);
System.err.println("** contentType:\t" + contentType);
System.err.println("** charset:\t" + charset);
System.err.println("");
logger.error("HttpURLUtils.sendGet happen exception, [{}]:", e);
throw new UtilException("HttpURLUtils.sendGet happen exception.");
} finally {
IOUtils.close(out);
IOUtils.close(in);
}
String resultStr = result.toString();
logger.debug("sendGet result [{}]", resultStr);
httpURLStatusBean.setResult(resultStr);
return httpURLStatusBean;
}
}
package com.example.favoritecode.jsapi;
import lombok.Data;
/**
* @Author ds
* @Date 2021-09-25
*/
@Data
public class UserIdKeyVo {
// 错 误码
private int errcode;
// 错 误 信息
private String errmsg;
// 用于后台交户表示用户姓名、身份证的凭证
private String user_id_key;
// user_id_key 有效期,过期需重新获取
private String expires_in;
}
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @Author ds
* @Date 2022-04-29
*/
@Data
@ApiModel(value = "UserIdKeyReuqestVo", description = "当事人")
public class UserIdKeyReuqestVo {
/**
* 姓名
*/
@ApiModelProperty(value="姓名", example="姓名", notes="姓名")
private String name;
/**
* 身份证号
*/
@ApiModelProperty(value = "身份证号", example = "15040219860826093X")
private String idNum;
}
import lombok.Data;
import java.io.Serializable;
/**
* @Author ds
* @Date 2021-09-25
*/
@Data
public class GetWxFaceInfoRequestVo implements Serializable {
private static final long serialVersionUID = 1L;
private String verifyResult;
private String certNo;
private String userIdKey;
@Override
public String toString() {
return "GetWxFaceInfoRequestVo{" +
"verifyResult='" + verifyResult + '\'' +
", certNo='" + certNo + '\'' +
", userIdKey='" + userIdKey + '\'' +
'}';
}
}
# 具体实现的impl部分代码
@Override
public UserIdKeyVo getUseridkey(UserIdKeyReuqestVo userIdKeyReuqestVo) {
String wx_appid = cfgService.getValue("wx_appid");
String wx_secret = cfgService.getValue("wx_secret");
return JsApi.getUserIdKey(wx_appid, wx_secret, userIdKeyReuqestVo.getName(), userIdKeyReuqestVo.getIdNum());
}
@Override
public GetWxFaceInfoVo getWxFaceInfo(GetWxFaceInfoRequestVo getWxFaceInfoRequestVo) {
String wx_appid = cfgService.getValue("wx_appid");
String wx_secret = cfgService.getValue("wx_secret");
GetWxFaceInfoVo getWxFaceInfoVo = JsApi.getInfo(wx_appid, wx_secret, getWxFaceInfoRequestVo.getVerifyResult());
if (getWxFaceInfoVo.getErrcode() == 0 && getWxFaceInfoVo.getIdentify_ret() == 0) {
logger.info("微信人脸核身 - 查询人脸核身信息 核验成功 开始更新数据");
// TODO 这里更新成功数据
}else {
// TODO 这里更新失败状态数据
}
logger.info("** 查询人脸核身信息->获取getInfo : " + JSON.toJSONString(getWxFaceInfoVo));
return getWxFaceInfoVo;
}