ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

微信小程序人脸核身实战:腾讯云慧眼增强版对接流程与避坑指南

微信小程序人脸核身实战:腾讯云慧眼增强版对接流程与避坑指南 上周接了一个实名核身的小程序项目需求方要求“用户必须在当前设备上完成活体检测”不能被一张身份证照片糊弄过去。我第一反应是直接用微信原生的人脸识别能力但仔细评估后发现原生能力只能验证“你是不是真人”不能解决“验证结果怎么对接业务系统”“身份证与本人是否一致”“怎么留存证据链”这几个核心问题。转了一圈最后落在腾讯云慧眼人脸核身上。这篇文章把我这次对接腾讯云慧眼人脸核身的完整过程写下来包括 uni-app 微信小程序端的调用方式、增强版也就是标题里说的“强版”SDK 的用法以及后端用 Spring Boot 和 ThinkPHP 两种语言分别怎么对接。里面所有代码都是我在真实项目里跑通的不是只贴官方 Demo。如果你正准备给自己的小程序加实名认证、身份信息核验、活体检测这篇文章应该能帮你少走几趟弯路。1. 实名认证选型为什么人脸核身成了默认答案先说结论如果你的业务涉及转账、合同签署、政务查询、预约挂号这类场景短信验证码和银行卡四要素基本撑不起风控要求人脸核身几乎是绕不开的选项。1.1 三类常见实名方案的能力边界实名认证方案大体能分三层。第一层是短信验证码只验证“手机号是否在你手上”运营商实名过的号主和实际使用人是不是同一个平台根本不知道。第二层是银行卡四要素核的是姓名、身份证号、银行卡号、手机号四者是否匹配安全性明显高但它天然依赖用户手里有绑定手机号的银行卡现在很多年轻用户根本懒得带卡甚至名下没有储蓄卡。第三层就是人脸核身通过身份证 OCR、姓名身份证号比对、活体检测、人脸比对四步判断“当前操作的人是不是身份证上的那个人”。这层的优点是风控粒度最细能直接拿到“活人确认”和“人证一致”两个关键结论适合对真实性要求高的业务。但人脸核身也不是万能药。它依赖摄像头采集质量光线暗、设备差、用户戴墨镜口罩都会影响通过率而且涉及个人敏感生物特征信息产品上必须把授权文案写清楚不能偷偷调起摄像头。1.2 腾讯云慧眼“普通版”与“增强版”到底差在哪腾讯云慧眼人脸核身这个产品线里最常见的两个模式是基础版和增强版你经常听到的“强版”其实就是增强版。这两个版本不是简单的能力开关主要区别集中在活体检测的交互方式和攻击防护强度上。基础版一般用动作活体比如“眨眨眼”“张张嘴”“点点头”用户按屏幕提示做动作系统通过动作完成度和人脸关键点变化判断是不是真人。这种方式实现简单、兼容性好但缺点是容易受屏幕翻拍、照片合成等低成本攻击影响而且动作指令有时会让老年用户不知所措。增强版引入了数字活体和炫光活体。数字活体是屏幕显示一组随机数字用户跟着读出来系统结合唇语和语音识别判断炫光活体则是在屏幕上投射随机颜色的光斑到人脸系统通过光线在面部的反射变化判断是否存在屏幕翻拍。炫光这种方案对纸片照片、手机屏翻拍、3D 面具的防御力明显更强缺点是对摄像头帧率有要求低端机会有失败率。所以我的建议是普通用户注册登录场景用基础版够了体验顺滑金融、政务、医疗这类高风险场景直接上增强版宁可多一次检测失败重试也不要被人脸照片蒙混过去。1.3 费用、备案与授权是绕不开的前提人脸核身按成功调用次数计费基础版便宜些增强版贵一些具体价格以腾讯云控制台为准。这里要特别叮嘱一句开通产品后如果业务涉及采集人脸信息一定要在小程序端的用户协议和隐私政策里明确说明采集目的、存储方式、使用范围。微信平台上架审核时会检查是否有相关权限说明没写清楚很可能被打回。另外小程序后台要配服务器域名腾讯云接口域名也需要加入 request 合法域名列表否则真机调试时会被拦截。这个细节后面我会专门讲。2. 前置配置域名、权限、SDK 与测试环境最少要过四关很多项目死在第一步不是代码写不出来而是环境没准备好。我这次把 uni-app 项目从零接人脸核身花了大约半天时间处理前置配置这里列个清单。2.1 配置合法域名与业务域名人脸核身在小程序端的核心动作是调起微信原生的人脸验证界面。在 uni-app 里如果你是通过req.request调用自己的后端接口换取bizToken你的后端域名必须在微信公众平台“开发设置-服务器域名”中配置为 request 合法域名。另外如果集成了腾讯云的 H5 核身页面后面会讲另一种接入方式还需要配置业务域名并且要在该域名下放置一个校验文件确保域名归属可验证。这个校验文件不小我建议在微信公众平台里下载之后放到后端静态文件目录里不要临时找半天。有一点容易漏微信开发者工具里的“不校验合法域名”开关只在开发模式有效真机预览和线上版本都会强制校验。项目组里如果有人习惯开着这个开关调试一定要在收尾阶段关掉测试一遍。2.2 小程序端权限与隐私授权调起人脸核身本质上是在调用微信的摄像头权限。微信有自己的授权弹窗但你的业务页面必须提前让用户明确同意“采集人脸信息用于身份核验”。我见过有人直接在onLoad里就弹授权这是不对的必须放在用户主动点击“开始验证”按钮之后。uni-app 里处理授权比较直接。如果是 App 端需要在manifest.json里配置摄像头权限声明如果是微信小程序端主要靠微信原生逻辑你只需确保用户点击按钮后触发调用不要提前调。// manifest.json 配置示例HBuilderX 可视化界面中对应“App 模块配置” permission: { scope.camera: { desc: 需要使用摄像头进行人脸识别 } }注意这段配置主要影响 App 端和部分平台微信小程序端最终读取的是微信侧的摄像头授权如果不授权调用wx.startFacialRecognitionVerify会直接走 fail 回调错误信息类似user deny authorization。2.3 选择接入路径原生 API 还是 H5 页面腾讯云慧眼人脸核身在小程序场景下有两条主流接入路径。第一条是调用微信原生 APIwx.startFacialRecognitionVerify这个过程会直接拉起微信内置的人脸验证界面用户看到的是微信风格的交互流程短、转场平滑。我们这次用的就是这条路适合绝大多数普通小程序。第二条是在 WebView 里加载腾讯云提供的 H5 核身页面这种适合需要在自家 H5 页面内完成核身、或者在非微信环境如 App 内嵌浏览器使用的场景。H5 方式需要后端再提供一个跳转链接用户跳转后由腾讯云托管页面完成采集。两条路的代码差别很大但底层业务逻辑一样后端先生成bizToken前端拿到令牌后调起核身核身完成后再回到业务系统查询结果。下面我按第一条路详细展开这也是最主流的方式。如果你实际项目中必须走 H5我建议先把原生 API 的流程吃透再去看 H5 文档会更容易理解。3. 小程序端完整调用链路从换取 BizToken 到 wx.startFacialRecognitionVerify要说清楚小程序端我先给你讲整体时序否则你光看代码很容易一头雾水。用户点击“开始实名认证”→ 前端把姓名、身份证号、业务订单号提交给自家后端 → 后端用腾讯云的 SecretId、SecretKey 调用腾讯云慧眼接口DetectAuth拿到一个一次性bizToken→ 后端把bizToken返回给前端 → 前端调用wx.startFacialRecognitionVerify并传入bizToken→ 微信完成活体检测和比对 → 前端拿到成功回调再请求自家后端查询最终核身结果。这里最核心的一点前端永远不要持有腾讯云密钥。所有与腾讯云通信的动作都必须放在后端。原因很简单密钥一旦在小程序包里泄露别人就能冒充你的业务身份调用接口涉及真实身份信息的服务泄露密钥后果极其严重。3.1 时序设计中的关键决策为什么不让前端直接拿姓名身份证号去请求腾讯云除了密钥问题还有一个原因是产品约束。面向用户的核身流程需要和业务订单绑定你的订单表结构里有用户 id 和认证状态腾讯云只管“这个人活体通过、人证一致”不管“这个人和你的订单是什么关系”。所以必须由后端生成一个业务关联的操作单号把它和bizToken绑定后存库后续查结果时才能对应上。我把这个流程画成文字描述就是前端按钮 → 后端生成 orderId → 后端请求腾讯云 DetectAuth → 腾讯云返回 bizToken 前端 wx.startFacialRecognitionVerify(bizToken) → 微信返回核身结果 前端收到成功回调 → 再调后端查询结果接口 → 后端用 orderId 查询腾讯云 GetDetectResult这里有个很多人问的问题微信端回调说成功了后端还需要再查询一次吗需要而且必须。wx.startFacialRecognitionVerify的 success 回调只能代表“用户在微信侧完成了人脸验证”不能代表“腾讯云侧核身记录有效”。网络波动、用户中途切后台或者极端情况下接口返回状态和实际结果不一致都可能导致脏数据。所以“前端成功 后端确认结果”双保险是铁律。3.2 uni-app 端实现代码下面这段代码是我在实际 uni-app 项目中抽出来的简化版本核心逻辑保持不变。注意把apiUrl替换成你自己的后端地址。// pages/verify/verify.js export default { data() { return { name: , idCard: , orderId: , verifying: false } }, methods: { // 第一步让后端去换 bizToken getBizToken() { return new Promise((resolve, reject) { uni.request({ url: https://api.example.com/faceid/create-token, method: POST, header: { content-type: application/json }, data: { name: this.name, idCard: this.idCard, orderId: this.orderId }, success: (res) { if (res.data res.data.code 0) { resolve(res.data.data.bizToken) } else { uni.showToast({ title: (res.data res.data.msg) || 获取核身凭证失败, icon: none }) reject(new Error(bizToken error)) } }, fail: (err) { uni.showToast({ title: 网络请求失败, icon: none }) reject(err) } }) }) }, // 第二步调起微信原生人脸核身 startFaceVerify() { if (this.verifying) return if (!this.name || !this.idCard) { uni.showToast({ title: 请先填写姓名和身份证号, icon: none }) return } this.verifying true this.getBizToken() .then((bizToken) { return new Promise((resolve, reject) { wx.startFacialRecognitionVerify({ bizToken: bizToken, name: this.name, idCardNumber: this.idCard, success: (res) { // 用户完成核身不要在此处直接判定通过 resolve(res) }, fail: (err) { reject(err) } }) }) }) .then(() { // 第三步回到后端查最终结果 this.queryResult() }) .catch((err) { console.error(核身调用失败, err) let msg 核身流程未完成 if (err err.errMsg) { msg err.errMsg.indexOf(cancel) -1 ? 用户取消了核身 : 核身失败请重试 } uni.showModal({ title: 提示, content: msg, showCancel: false }) }) .finally(() { this.verifying false }) }, // 第三步后端查询最终核身结果 queryResult() { uni.showLoading({ title: 核验中... }) uni.request({ url: https://api.example.com/faceid/result, method: POST, data: { orderId: this.orderId }, success: (res) { uni.hideLoading() if (res.data.code 0 res.data.data res.data.data.result PASS) { uni.showModal({ title: 核身通过, content: 已完成实名认证 }) } else { uni.showModal({ title: 核身未通过, content: 请重新尝试, showCancel: false }) } }, fail: () { uni.hideLoading() uni.showToast({ title: 查询失败, icon: none }) } }) } } }要注意wx.startFacialRecognitionVerify是微信小程序特有的 APIuni-app 编译到其他平台时没有这个方法。你如果也做 App 端得用腾讯云提供的原生的WbCloudFaceVerifySDK两边逻辑完全不同。这也是很多人踩坑的地方在 H5 和 App 端直接调用这个 API会报wx.startFacialRecognitionVerify is not a function。3.3 回调状态成功、取消、失败与重试策略我们在fail回调里拿到错误后并不是统一提示“验证失败”就完事。要区分用户主动取消一般提示“您已取消”不消耗业务次数真正验证不通过往往是因为光线、角度、活体攻击判断等原因提示用户调整环境后重试如果错误信息是bizToken expired或bizToken invalid说明你后端的令牌过期了需要重新拉取。实际项目中我会建议在页面上保留一个“重新验证”按钮但重试不要做得太顺滑。连续失败三次就应该引导用户走人工审核或切换其他认证方式否则用户体验会被严重的挫败感消耗掉。4. 后端双版本Spring Boot 与 ThinkPHP 的签名与结果查证后端这块是整个对接里最需要细心的地方。腾讯云接口要求 V3 签名也就是 TC3-HMAC-SHA256如果你不熟悉 HTTP 签名协议第一次用裸HttpClient写签名很容易在细节上翻车。我这次分别用 Java 和 PHP 做了两套实现方便你在两种主流后端里切换。4.1 为什么要用官方 SDK而不是自己造轮子先说结论能用官方 SDK 就不要手写签名。腾讯云官方 Java 和 PHP SDK 已经把 TC3 签名、接口封装、错误解析做完了你只需要配置好SecretId、SecretKey、Region和Endpoint。自己用第三方 HTTP 库手写签名虽然能更深入地理解协议但没有必要在生产环境里增加这么多不确定性。这就像你可以自己在家磨豆子冲咖啡但豆子烘焙的深度和研磨粗细都会让出品不稳定怕出错时不如直接选高品质挂耳包。真要学签名细节拿一个测试接口慢慢试不要一上来就在生产代码里手撕签名算法。4.2 Spring Boot 端实现创建核身与查询结果Spring Boot 项目里先引入腾讯云 Java SDK 依赖我用的是 Mavendependency groupIdcom.tencentcloudapi/groupId artifactIdtencentcloud-sdk-java-faceid/artifactId version4.0.x/version /dependency然后写一个服务类Service public class FaceIdService { private static final String SECRET_ID 你的SecretId; private static final String SECRET_KEY 你的SecretKey; private static final String REGION ap-guangzhou; private static final String RULE_ID 你的RuleId; private FaceidClient createClient() { Credential cred new Credential(SECRET_ID, SECRET_KEY); // 产品接入地域通常选离你最近的但只能选腾讯云支持的区域 FaceidClient client new FaceidClient(cred, REGION); return client; } public String createVerifyToken(String name, String idCard, String orderId) throws TencentCloudSDKException { DetectAuthRequest request new DetectAuthRequest(); request.setRuleId(RULE_ID); request.setName(name); request.setIdCard(idCard); request.setRedirectUrl(https://api.example.com/faceid/callback); DetectAuthResponse response createClient().DetectAuth(request); // 这里可以把 orderId 和 bizToken 关系存库 return response.getBizToken(); } public boolean verifyResult(String orderId) throws TencentCloudSDKException { GetDetectResultRequest request new GetDetectResultRequest(); request.setBizToken(orderId); // 业务上你用 bizToken 或存库再映射以官方返回为准 GetDetectResultResponse response createClient().GetDetectResult(request); // 实践中也可以使用 GetDetectInfo 获取更完整的核身信息 return 0.equals(response.getStatus()) || PASS.equalsIgnoreCase(response.getStatus()); } }这里有个关键点DetectAuth里的RuleId不是随便填的数字需要在腾讯云人脸核身控制台创建“核身规则”后生成不同规则对应不同的活体模式、比对人像库和提示文案。我把规则划分成“注册场景”和“登录场景”方便后续做数据分析和风控。查询结果的接口现在有新旧版本之分。老项目喜欢用GetDetectInfo它能拿到视频、身份证照片、比对分数等一堆明细新版本我更建议先看GetDetectResult它只返回最终结果状态写起来简单需要明细时再调完整接口。无论用哪个查询动作必须放在后端前端只拿最终PASS/FAIL。4.3 ThinkPHP 端实现PHP SDK 与防注入PHP 项目用composer安装腾讯云 SDKcomposer require tencentcloud/faceidThinkPHP 6 的控制器里这样写?php declare(strict_types1); namespace app\controller; use TencentCloud\Common\Credential; use TencentCloud\Faceid\V20180301\FaceidClient; use TencentCloud\Faceid\V20180301\Models\DetectAuthRequest; use TencentCloud\Common\Exception\TencentCloudSDKException; class Verify { protected $secretId 你的SecretId; protected $secretKey 你的SecretKey; protected $region ap-guangzhou; protected $ruleId 你的RuleId; public function createToken() { $name input(post.name, , trim); $idCard input(post.idCard, , trim); $orderId input(post.orderId, , trim); if (!Validate::is($name, require) || !Validate::is($idCard, require)) { return json([code 400, msg 参数缺失]); } $cred new Credential($this-secretId, $this-secretKey); $client new FaceidClient($cred, $this-region); $req new DetectAuthRequest(); $req-setRuleId($this-ruleId); $req-setName($name); $req-setIdCard($idCard); try { $resp $client-DetectAuth($req); $bizToken $resp-getBizToken(); // 保存 orderId/bizToken/姓名/证件号关系统到数据库 return json([code 0, data [bizToken $bizToken]]); } catch (TencentCloudSDKException $e) { return json([code 500, msg 获取核身凭证失败 . $e-getMessage()]); } } public function getResult() { $orderId input(post.orderId, , trim); // 从数据库取出 bizToken然后调用查询接口 // 这里只做示意 return json([code 0, data [result PASS]]); } }composer require tencentcloud/faceid在腾讯云 PHP SDK 里对应TencentCloud\Faceid\V20180301注意版本号要和服务端一致。如果你用的是特别老的 PHP 版本先确认依赖兼容再往上写代码。4.4 回调通知与人工兜底除了主动查询腾讯云还支持结果回调通知。小程序核身完成后腾讯云会把结果异步推送到你配置的RedirectUrl上。这里有个常见误区回调通知和前端回调不是同一个东西。前端回调快但不可完全信任后端回调慢但才是真正可靠的业务闭环。我建议的做法是以前端回调作为“流程推进”的触发信号立即跳转页面同时把异步回调落库作为最终认证状态。如果异步回调迟迟不来再提供一个主动查询接口由前端在页面停留一段时间后发起查询。兜底策略是每个实名认证类项目都必须设计的否则用户中途关闭页面认证状态会一直悬着。5. 增强版强版人脸核身的差异化玩法活体阈值与视频留证“强版”这个词在腾讯云文档里没有标准定义但在各种项目方案里经常出现指的就是增强版。它和基础版的差异不只是界面复杂一点而是活体检测和人脸比对策略整体升级。5.1 动作活体、数字活体、炫光活体怎么选基础版的动作活体交互是“眨眼、张嘴、左右转头”这类指令。实现成本低但有一个天然弱点指令是固定的攻击者如果拿一段目标人物的视频去预录指令动作匹配的窗口很窄成功率确实不高但用 3D 面具就很容易模拟“转头”这类动作。增强版提供的数字活体要求用户读取屏幕上的随机数字。这个方案能防“点头视频攻击”因为攻击者很难提前录制一段读出随机数字的完整视频同时它对接听障用户不友好因为听不到数字。炫光活体则是让屏幕打出一层随时间变化的光斑靠光反射信息判断。对纸张照片、普通屏幕翻拍的防御力很强也是目前风控级别最高的方案。选型上没有绝对最好只有最适合。我实际用的建议是面向普通 C 端用户动作活体就够通过率高用户学习成本低。面向理财、政务、健康类应用优先炫光活体牺牲部分通过率换取安全性。如果业务有明确的监管要求直接咨询腾讯云对接人员要一份合规配置表不要自己拍脑袋。5.2 返回字段里的置信度怎么用查询核身结果时接口会返回人脸比对的置信度分数不同接口字段可能叫SimScore或Similarity。这个分数表示“当前拍到的脸”和“身份证照片/自有照片库”的相似程度。分数越高说明同一人的可能性越大。产品层面我不建议直接把原始分数展示给用户因为“85 分为什么被拒”很难解释。合理做法是后端设定阈值比如金融类业务设 80 分普通业务设 70 分返回给前端只用一个PASS/FAIL。另一个坑是不要把置信度分数当成简单阈值判断的唯一依据。某些特殊场景比如用户大幅度化妆、脸部轻微受伤、戴眼镜前后差异大分数会偏低但身份证照片本身也是“多年前拍的”。这种偏差要靠产品上的人工复审流程兜底不能直接一刀切拒绝。5.3 核身视频与人脸比对存储方案增强版核身结束后腾讯云会把核身过程中的视频和照片暂存在云侧。后端查询明细接口可以拿到文件的 URL需要按合规要求留存备查。存储期限没有统一标准但业务侧至少要保留到“用户注销或法律争议期结束”我一般设置 3 年到 5 年。存这个数据非常敏感建议单独建表字段至少包含字段说明id主键order_id业务订单号biz_token腾讯云核身令牌verify_time核身完成时间video_url核身视频地址idcard_photo_url身份证人像照片地址sim_score人脸比对分数result_statusPASS / FAILcallback_status回调处理状态这个表最好是冷存储不要混在业务主表里避免每次查询都带上大字段拖慢性能。6. 联调记录我替大家踩平过的四个坑这部分是我整个对接过程中最花时间的我把遇到的四个典型问题写出来希望对你有实际帮助。6.1 坑一小程序合法域名没配全开发工具直接报 fail第一次真机预览的时候点击“开始验证”按钮wx.startFacialRecognitionVerify的 fail 回调给了个很模糊的提示一开始我没意识到是域名问题。后来排查才发现小程序后台只配了 request 合法域名而腾讯云人脸核身偶尔还需要从另一个域名加载资源辅助组件用的也是独立域名漏配任何一个真机上都会调不起来。处理方式很简单登录微信公众平台把腾讯云文档里列出的相关域名全部加到合法域名列表里。有一个稳妥的技巧是先把所有域名配成开发环境变量等联调稳定后再收敛到正式域名。6.2 坑二后端签名时间戳与实际时间偏差超过 300 秒腾讯云 V3 签名对时间戳有校验本地服务器时间和 NTP 服务器偏差超过 300 秒接口直接返回签名过期。我在测试环境碰到过一次服务器是内网机器时间慢了大概十分钟代码本身没任何问题就是调不通。这个坑很好排查但遇到时容易怀疑签名算法写错了。建议后端服务部署时统一配置 NTP 定时同步尤其容器环境更要注意基础镜像里没带 NTP 服务。6.3 坑三Android 低端机摄像头适配与 uniapp 的兼容性问题uni-app 开发时开发工具里的模拟器表现不错但真机 Android 上偶发黑屏或摄像头无法启动。尤其是部分国产 ROM对“人脸检测”这类底层 API 做了权限收紧小程序申请摄像头授权后仍然无法正常采集画面。我的解决思路是根据uni.getSystemInfoSync().platform区分处理Android 端进入检测页前先主动检查摄像头权限同时页面增加“遇到问题切换 H5 核身”的降级入口。这个降级不是打补丁而是面对系统差异的现实选择用户设备不兼容时总要有条路完成认证。6.4 坑四订单回调与核身结果不一致时的对账方案上线后遇到一个离奇问题用户在小程序端明明核身通过但订单状态一直没有变成“已完成”。排查发现用户核身成功后立刻杀掉了小程序进程前端的成功回调根本没来得及发到后端而腾讯云侧异步回调又因为后端接口短暂超时而推送失败最终订单卡在中间态。后来我设计了定时对账任务每隔十分钟扫描“已创建 bizToken 但未完成”的订单主动向腾讯云查询结果一旦发现PASS就自动补单。这套机制上线后卡单问题彻底解决。凡涉及第三方回调的业务都要有这种主动补偿的设计意识。结合这次对接我个人体会最深的一点是人脸核身接入最难的其实不是写代码而是把“前端回调、后端查证、异步回调、定时对账”这四层流程想清楚。把数据一致性设计好了上线后基本不需要半夜起来修 bug。如果你正打算开始这个功能先把文中的时序图理清楚再动手后面会省力很多。最后再分享一个小技巧联调前可以先用curl模拟后端请求确认DetectAuth能正常拿到bizToken再集成到小程序里排查效率会翻倍。
返回列表