微信小程序人脸认证1.0迁移2.0
最近老是收到微信平台的通知信息,告知“原微信人脸核身接口将于2026年6月30日停止服务,逾期未切换将无法使用人脸核身功能。”,看了一下官网的文档,2.0的使用变得更复杂了,所以整理了一下需要使用到的接口和流程,文中代码仅展示核心逻辑和框架,需根据实际项目调整。
概览
2.0 接口共需要使用以下四个接口:
| 名称 | 作用 | 文档 | 前后端 |
|---|---|---|---|
| getAccessToken | 获取接口调用凭据 | 官网 | 后端 |
| getVerifyId | 获取用户人脸核身会话唯一标识 | 官网 | 后端 |
| wx.requestFacialVerify | 发起人脸验证 | 官网 | 前端 |
| queryVerifyInfo | 查询人脸认证结果 | 官网 | 后端 |
调用流程
- 后端获取 AppId、AppSecret
- 后端用 AppId、AppSecret 调用
getAccessToken获取 AccessToken - 后端用 AccessToken 调用
getVerifyId获取 verifyId,并持久化 out_seq_no、openid、cert_info 与 verifyId 的映射 - 后端将 verifyId 返回给前端,前端调用
wx.requestFacialVerify发起人脸认证(需在 1 小时内完成) - 前端认证完成后通知后端,后端用 verifyId 查出关联数据,调用
queryVerifyInfo查询真实结果
时序图
后端接口清单
后端至少需要提供两个接口给前端:
- 获取 verifyId
- 查询真实认证结果
获取 AppId、AppSecret
在微信公众平台 → 开发与服务 → 开发管理页面中获取:
- AppID:固定值,直接复制
- AppSecret:需要手动生成
⚠️ 如果 AppID 或 AppSecret 不慎泄露,立即重置 AppSecret。重置后旧的 AppSecret 无法使用,所有使用到的业务系统都需要更换。
调用 getAccessToken 接口
请求信息
- 请求方式:GET
- 接口地址:
https://api.weixin.qq.com/cgi-bin/token
请求参数
| 参数 | 类型 | 必填 | 说明 | 来源 |
|---|---|---|---|---|
| appid | string | 是 | 小程序唯一凭证,即 AppID | 获取 AppId、AppSecret |
| secret | string | 是 | 小程序唯一凭证密钥,即 AppSecret | 获取 AppId、AppSecret |
| grant_type | string | 是 | 固定值 client_credential |
固定值 |
返回参数
| 参数 | 类型 | 说明 |
|---|---|---|
| access_token | string | 获取到的凭证 |
| expires_in | number | 凭证有效时间,单位秒。目前为 7200 秒(2 小时) |
返回示例:
{
"access_token": "ACCESS_TOKEN",
"expires_in": 7200
}
💡 普通 token:2 小时有效,过期前需提前刷新,建议本地缓存并提前 5 分钟刷新避免边界失效。
💡 稳定版 token:长期有效,推荐生产环境使用。详见 获取稳定版接口调用凭据。
示例代码
核心逻辑:拼接 URL 请求微信接口,解析返回的 access_token 并缓存。
public String getAccessToken() throws Exception {
String url = String.format(
"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s",
appId, appSecret);
// 发送 GET 请求,解析 JSON 返回 access_token
// 建议本地缓存,提前 5 分钟刷新,避免边界过期
// access_token 过期时需自动刷新重试
}
调用 getVerifyId 接口
请求信息
- 请求方式:POST
- 接口地址:
https://api.weixin.qq.com/cityservice/face/identify/getverifyid?access_token=TOKEN
请求参数
URL 参数:
| 参数 | 类型 | 必填 | 说明 | 来源 |
|---|---|---|---|---|
| access_token | string | 是 | 接口调用凭证 | getAccessToken 返回 |
请求体参数:
| 参数 | 类型 | 必填 | 说明 | 来源 |
|---|---|---|---|---|
| out_seq_no | string | 是 | 业务侧唯一标识(一个 AppID 下唯一),5-32 字符,只能包含数字、大小写字母、_、- |
业务侧生成 |
| openid | string | 是 | 用户身份标识,用户登录小程序后获取,详见 code2session | 小程序登录获取 |
| cert_info | object | 是 | 用户身份信息,见下表 | 前端传入 |
cert_info 对象:
| 参数 | 类型 | 必填 | 说明 | 来源 |
|---|---|---|---|---|
| cert_type | string | 是 | 证件类型,身份证固定为 IDENTITY_CARD |
固定值 |
| cert_name | string | 是 | 姓名 | 前端传入 |
| cert_no | string | 是 | 身份证号 | 前端传入 |
请求示例:
{
"out_seq_no": "550e8400e29b41d4a716446655440000",
"openid": "o6_bmjrPTlm6_2sgVt7hMZOPfL2M",
"cert_info": {
"cert_type": "IDENTITY_CARD",
"cert_name": "张三",
"cert_no": "110101199001011234"
}
}
⚠️ out_seq_no 要求 5-32 字符,UUID.randomUUID().toString() 生成的是 36 字符(含横杠),超出限制会导致接口返回 84011 out_seq_no 格式无效。应去掉横杠(32 字符)或使用时间戳+随机数生成。
返回参数
| 参数 | 类型 | 说明 |
|---|---|---|
| errcode | number | 错误码,0 表示成功 |
| errmsg | string | 错误信息 |
| verify_id | string | 人脸核身会话唯一标识 |
| expires_in | number | verify_id 有效时间,单位秒,默认 3600(1 小时) |
返回示例:
{
"errcode": 0,
"errmsg": "ok",
"verify_id": "verify_id_xxxx",
"expires_in": 3600
}
⚠️ verify_id 有效期为 1 小时,前端获取后应尽快调起人脸认证,超时后需重新获取。
示例代码
核心逻辑:生成 out_seq_no,携带 access_token 和实名信息 POST 请求,返回 verify_id。成功后需持久化 out_seq_no、openid、cert_info 与 verify_id 的映射关系,供 queryVerifyInfo 使用。
public String getVerifyId(String openid, String certName, String certNo) throws Exception {
String accessToken = getAccessToken();
String url = "https://api.weixin.qq.com/cityservice/face/identify/getverifyid?access_token=" + accessToken;
// out_seq_no 要求 5-32 字符,UUID 去横杠刚好 32 字符
String outSeqNo = UUID.randomUUID().toString().replaceAll("-", "");
String body = objectMapper.writeValueAsString(Map.of(
"out_seq_no", outSeqNo,
"openid", openid,
"cert_info", Map.of(
"cert_type", "IDENTITY_CARD",
"cert_name", certName,
"cert_no", certNo
)
));
// POST 请求,解析返回的 verify_id
// 需要根据 errcode 做错误分类处理(如 access_token 过期自动刷新重试)
// 成功后持久化映射关系,供 queryVerifyInfo 使用
// redis.set("FACE:VERIFY:" + verifyId, {outSeqNo, openid, certName, certNo}, 3600s);
}
发起人脸认证
此步骤在小程序前端完成,需要先从后端获取到 verifyId。
请求信息
调用方法:
wx.requestFacialVerify(Object object)
参数说明
| 参数 | 类型 | 必填 | 说明 | 来源 |
|---|---|---|---|---|
| verifyId | string | 是 | 人脸核身会话唯一标识 | getVerifyId 返回 |
| success | function | 否 | 接口调用成功的回调函数 | — |
| fail | function | 否 | 接口调用失败的回调函数 | — |
| complete | function | 否 | 接口调用结束的回调函数(成功/失败都会执行) | — |
⚠️ success 回调仅代表前端拉起人脸认证成功,不代表人脸验证通过。需要在 success 中通知后端调用 queryVerifyInfo 查询真实结果。
示例代码
前端完整调用流程:先请求后端获取 verifyId → 调起人脸认证 → 成功后通知后端查询结果。
// 1. 请求后端获取 verifyId
wx.request({
url: 'https://your-server.com/api/face/get-verify-id',
method: 'POST',
data: { openid, certName, certNo },
success(res) {
// 2. 调起人脸认证(verifyId 有效期 1 小时,获取后尽快调起)
wx.requestFacialVerify({
verifyId: res.data.verifyId,
success() {
// 3. 认证成功,通知后端查询真实结果(后端根据 verifyId 查出完整参数)
wx.request({
url: 'https://your-server.com/api/face/query-result',
method: 'POST',
data: { verifyId: res.data.verifyId },
success(result) {
if (result.data.verifyRet === 10000) {
console.log('认证成功');
}
}
});
},
fail() {
console.log('人脸认证失败');
}
});
}
});
调用 queryVerifyInfo 接口
请求信息
- 请求方式:POST
- 接口地址:
https://api.weixin.qq.com/cityservice/face/identify/queryverifyinfo?access_token=TOKEN
请求参数
URL 参数:
| 参数 | 类型 | 必填 | 说明 | 来源 |
|---|---|---|---|---|
| access_token | string | 是 | 接口调用凭证 | getAccessToken 返回 |
请求体参数:
| 参数 | 类型 | 必填 | 说明 | 来源 |
|---|---|---|---|---|
| verify_id | string | 是 | 人脸核身会话唯一标识 | getVerifyId 返回 |
| out_seq_no | string | 是 | 业务侧唯一标识,必须与 getVerifyId 传入的一致 | getVerifyId 时持久化的 out_seq_no |
| openid | string | 是 | 用户身份标识,必须与 getVerifyId 传入的一致 | getVerifyId 时持久化的 openid |
| cert_hash | string | 是 | 证件信息摘要,需手动计算(计算规则见下方) | 由持久化的 cert_info 计算得出 |
cert_hash 计算规则
对 cert_info 中的 cert_type、cert_name、cert_no 分别进行 Base64 编码(中文需先 UTF-8 编码),然后按顺序拼接:
cert_type=BASE64(cert_type)&cert_name=BASE64(cert_name)&cert_no=BASE64(cert_no)
对拼接结果进行 SHA-256 哈希,输出十六进制小写字符串。
返回参数
| 参数 | 类型 | 说明 |
|---|---|---|
| errcode | number | 错误码,0 表示成功 |
| errmsg | string | 错误信息 |
| verify_ret | number | 验证结果,10000 表示识别成功 |
⚠️ 核身通过的判断条件:errcode = 0 且 verify_ret = 10000。微信后台会校验 cert_info 和 openid,不一致则返回对应 errcode 而非 verify_ret,防止身份信息被篡改。
示例代码
核心逻辑:后端根据 verifyId 从缓存查出 out_seq_no、openid、cert_info,计算 cert_hash 后调用微信接口,判断 verify_ret 是否为 10000。
public int queryVerifyInfo(String verifyId) throws Exception {
// 从缓存取出 getVerifyId 时持久化的关联数据
Map<String, String> record = redis.get("FACE:VERIFY:" + verifyId);
String outSeqNo = record.get("out_seq_no");
String openid = record.get("openid");
String certName = record.get("cert_name");
String certNo = record.get("cert_no");
String accessToken = getAccessToken();
String url = "https://api.weixin.qq.com/cityservice/face/identify/queryverifyinfo?access_token=" + accessToken;
// 根据 cert_type(固定IDENTITY_CARD)、cert_name、cert_no 计算 cert_hash
String certHash = computeCertHash(certName, certNo);
String body = objectMapper.writeValueAsString(Map.of(
"verify_id", verifyId,
"out_seq_no", outSeqNo,
"openid", openid,
"cert_hash", certHash
));
// POST 请求,解析返回的 verify_ret,10000 表示认证成功
// 需要根据 errcode 做错误分类处理
}
// cert_type 固定为 IDENTITY_CARD,cert_name 和 cert_no 为用户实际信息
private String computeCertHash(String certName, String certNo) throws Exception {
String raw = "cert_type=" + Base64.getEncoder().encodeToString("IDENTITY_CARD".getBytes(StandardCharsets.UTF_8))
+ "&cert_name=" + Base64.getEncoder().encodeToString(certName.getBytes(StandardCharsets.UTF_8))
+ "&cert_no=" + Base64.getEncoder().encodeToString(certNo.getBytes(StandardCharsets.UTF_8));
byte[] hash = MessageDigest.getInstance("SHA-256").digest(raw.getBytes(StandardCharsets.UTF_8));
StringBuilder hex = new StringBuilder();
for (byte b : hash) hex.append(String.format("%02x", b));
return hex.toString();
}
总结
完整的认证流程:
- 后端保存 AppId、AppSecret
- 后端用 AppId、AppSecret 获取 AccessToken(建议缓存,2 小时有效期;生产环境推荐使用稳定版 token)
- 前端传递用户实名信息给后端,后端调用 getVerifyId 获取 verifyId,并持久化 out_seq_no、openid、cert_info 与 verifyId 的映射
- 后端将 verifyId 返回给前端(verifyId 有效期 1 小时,前端需尽快调起认证)
- 前端调用
wx.requestFacialVerify发起人脸认证 - 前端认证成功后通知后端(仅传 verifyId 即可)
- 后端根据 verifyId 从缓存查出完整参数,调用 queryVerifyInfo 查询真实结果并返回给前端
后端需要提供的接口:
POST /api/face/get-verify-id— 返回 verifyIdPOST /api/face/query-result— 前端仅传 verifyId,后端自行查缓存补齐参数后查询真实结果

浙公网安备 33010602011771号