生物认证是微信小程序安全能力中非常实用的一环,指纹解锁、人脸验证这些能力背后,依赖的是微信提供的SOTER生物认证体系。不少开发者在使用过程中会碰到各种奇怪的问题:明明调用了认证接口却拿到JSON解析错误,后台验签总是失败,或者用户换了指纹之后认证状态全乱了。这篇文章围绕verifySignature这个核心环节,把整个生物认证的接入流程、验签原理和常见误区讲清楚。

一、先弄清楚verifySignature在整个流程中的位置
很多开发者一上来就到处找verifySignature的文档,结果发现微信官方并没有一个叫这个名字的小程序端API,这是第一个常见误区。实际上,verifySignature是微信后台提供的校验接口,它对应的HTTP接口地址是https://servicewechat.com/wxa/checkverifyinfo,真正在小程序端调用的API是wx.startSoterAuthentication。
整个流程可以拆成三步。第一步,小程序端调用wx.checkIsSupportSoterAuthentication检测设备是否支持生物认证,支持的模式包括指纹(fingerPrint)和人脸(facial,仅部分安卓设备支持)。第二步,调用wx.startSoterAuthentication拉起认证,认证成功后回调中会返回一个result字段,它是一个JSON字符串,里面包含raw和signature两段关键数据。第三步,把这段result完整地传给自家后台,由后台调用verifySignature接口完成签名校验。很多问题的根源就在于开发者把这三步的职责搞混了,比如试图在小程序端本地校验签名,这从安全角度讲毫无意义,因为客户端的一切数据都可能是被篡改的。
下面是一个小程序端的标准调用示例:
wx.checkIsSupportSoterAuthentication({
success(res) {
if (res.supportMode.indexOf('fingerPrint') >= 0) {
wx.startSoterAuthentication({
requestAuthModes: ['fingerPrint'],
challenge: 'your-challenge-string', // 服务端下发的随机串,用于防重放
authContent: '请验证指纹以确认支付',
success(res) {
// result是JSON字符串,需要原样传给后台
console.log(res.resultJSON)
console.log(res.resultJSONSignature)
wx.request({
url: 'https://your-server.com/api/verify',
data: {
resultJSON: res.resultJSON,
resultJSONSignature: res.resultJSONSignature
}
})
},
fail(err) {
console.log(err.errMsg) // 认证失败或取消
}
})
}
}
})
注意回调里返回的是两个独立字段:resultJSON是认证结果的JSON字符串,resultJSONSignature是对这段JSON的签名。两者必须成对传给后台,缺一不可。
二、后台验签的实现细节与签名算法
后台拿到resultJSON和resultJSONSignature之后,需要先解析JSON提取出关键字段,再调用微信的校验接口。resultJSON解析后的结构大致是这样的:包含raw、fgps(指纹组标识)、json_desc、exp(有效期时间戳)等字段。其中raw字段本身又是一段JSON字符串,里面有fid(本次认证的标识)和ts(时间戳)。
这里有个非常容易踩的坑:verifySignature接口要求传入的json_string必须和签名时用的字符串逐字节一致。有些开发者在后台拿到resultJSON后先做了一次JSON反序列化再重新序列化,字段顺序、空格、转义都可能发生变化,导致校验必然失败。正确做法是直接把小程序端传来的原始字符串透传给微信接口,不要做任何二次处理。
后台调用示例(以Node.js为例):
const axios = require('axios')
async function verifySignature(openid, jsonStr, signature) {
const token = await getAccessToken() // 获取access_token
const url = 'https://api.weixin.qq.com/cgi-bin/soter/verify_signature?access_token=' + token
const res = await axios.post(url, {
openid: openid,
json_string: jsonStr, // 必须是原始字符串,不能重新序列化
json_signature: signature
})
// res.data.is_ok 为 true 表示校验通过
return res.data.is_ok === true
}
如果不想依赖微信接口,也可以走本地验签的方案。签名的公钥可以通过wx.getSoterPublicKey相关流程获取(或使用微信公开的验签公钥),签名算法是RSA,对raw字段做SHA256摘要后再验签。本地验签的好处是不依赖网络请求,但需要自己维护公钥的更新,一旦微信侧轮换公钥而你的服务没有同步,验签就会全线失败,所以一般还是推荐直接调官方接口。
三、高频踩坑点逐一排查
第一个高频问题是JSON解析失败。典型报错是后台解析resultJSON时抛异常,原因往往是小程序端把resultJSON当成普通字符串进行了二次编码,或者用JSON.parse解析签名相关的Base64内容。记住一点:resultJSONSignature是Base64编码的签名字符串,不是JSON,不要对它做JSON.parse。
第二个问题是challenge的滥用。challenge参数是防重放攻击的关键,必须由服务端生成、保证一次性使用,并且在校验时核对resultJSON中回传的challenge是否一致。如果图省事在前端写死一个固定字符串,攻击者截获一次认证数据就可以反复重放,生物认证的安全性就完全失效了。正确做法是每次认证前向后台申请一个随机challenge,后台记录下来,验签通过后立刻作废。
第三个问题是指纹状态变化。用户在系统设置里新增或删除指纹后,fgps会发生变化,之前缓存的认证状态就不再可信。业务逻辑上要注意:不能因为用户以前验证过指纹就跳过本次认证,每次敏感操作都应该重新走完整的认证流程。另外在iOS上,设备指纹集合变化后微信侧会感知到,可能出现认证直接失败的情况,这是正常的安全机制,需要引导用户重新认证。
第四个问题是错误码处理不细致。wx.startSoterAuthentication的fail回调中,errMsg可能是authing(正在认证中)、cancel(用户取消)等不同情况,如果统一弹一个“认证失败”的提示,用户体验会很差。建议针对用户取消单独处理,静默返回即可,不要当成错误反复弹窗。
四、上线前必须核对的事项清单
发布前建议对照以下几点逐一检查。设备兼容性方面,必须在不支持SOTER的设备上提供降级方案,比如退回支付密码验证,checkIsSupportSoterAuthentication的失败分支一定要写好兜底逻辑。数据传输方面,resultJSON从小程序到后台的传输要保证完整性,如果经过网关或框架的自动解析,务必确认拿到的是原始字符串。
access_token管理方面,verifySignature接口依赖access_token,这个token有调用频率限制,应该全局缓存、集中刷新,不要每次验签都重新获取,否则高并发场景下很容易触发限流。日志方面,验签失败时要记录完整的errcode和原始报文,微信接口返回的错误码能帮助快速定位是参数问题还是签名问题,切忌只记一个“验签失败”。
最后提醒一点安全边界:生物认证通过只代表“当前操作者通过了指纹或人脸验证”,不代表业务上的绝对安全。challenge防重放、HTTPS传输、后台业务校验这些环节一个都不能省。把verifySignature当成安全链路中的一环而不是全部,才能真正发挥SOTER体系的价值。
微信小程序生物认证verifySignature避坑指南修改时间:2026-09-07 11:14:55