金融、医疗、教育等行业的官网,常被法规要求对用户做实名认证——核实"你是你声称的那个人"。实现方式从简到繁依次是:身份证二要素核验(姓名+身份证号)、三要素(姓名+身份证号+手机号)、人脸活体检测。具体选哪种,取决于业务对身份真实性的要求强度。本篇以尧图医疗类项目的经验,讲清实名认证的对接要点。
一、身份证二要素核验
二要素核验是最基础的实名认证:把用户输入的姓名和身份证号提交给第三方接口,接口对接公安系统比对,返回一致或不一致。这是性价比最高的认证方式,单次调用几分钱,足以满足大多数合规要求。阿里云、腾讯云、百度云都提供这类 API,调用方式大同小异。
// 身份证二要素核验(阿里云市场)
async function verifyIdCard(name, idCard) {
// 1. 基础格式校验,省一次接口调用费
if (!/^[\u4e00-\u9fa5]{2,6}$/.test(name)) {
return fail('姓名格式不正确');
}
if (!/^\d{17}[\dXx]$/.test(idCard)) {
return fail('身份证号格式不正确');
}
// 2. 身份证号校验位验证(GB 11643-1999标准)
if (!checkIdCardChecksum(idCard)) {
return fail('身份证号校验失败');
}
// 3. 调用第三方核验接口
const result = await http.post('https://idcard.market.alicloudapi.com/IDCard', {
name, idCard
}, {
headers: { 'Authorization': 'APPCODE ' + APP_CODE }
});
// 4. 解析结果
if (result.code === 0 && result.data.isMatch === '01') {
return success('实名认证通过');
}
return fail('姓名与身份证号不匹配');
}
// 身份证校验位算法
function checkIdCardChecksum(id) {
const weight = [7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2];
const check = ['1','0','X','9','8','7','6','5','4','3','2'];
let sum = 0;
for (let i = 0; i < 17; i++) sum += parseInt(id[i]) * weight[i];
return id[17].toUpperCase() === check[sum % 11];
}
先做本地格式校验再调接口,能省下不少无效调用——尧图统计过,约 20% 的请求是格式错误(手滑输错、位数不对)。身份证号自带校验位,用算法验证就能挡住大部分乱填的号,既省钱又快。校验通过后,认证结果要存库(姓名、身份证号脱敏存储),避免重复认证重复扣费。
二、OCR 识别与活体检测
二要素核验只能保证"姓名和身份证号匹配",但无法确认操作者本人就是身份证主人——身份证号泄露的情况并不少见。要确认"本人在操作",需要 OCR 识别身份证图片 + 人脸活体检测。OCR 把证件照上的信息提取出来,省去用户手动输入;活体检测要求用户做动作(眨眼、张嘴、转头),证明是真人而非照片。
// 完整实名认证流程:OCR → 二要素 → 活体检测
async function fullVerification(idCardFront, idCardBack, faceVideo) {
// 步骤1:OCR识别身份证正反面
const ocrResult = await ocrApi.recognizeIdCard(idCardFront, idCardBack);
if (!ocrResult.success) return fail('身份证识别失败,请重新拍照');
const { name, idCard, address, expireDate } = ocrResult.data;
// 步骤2:检查证件是否过期
if (new Date(expireDate) < new Date()) {
return fail('身份证已过期');
}
// 步骤3:二要素核验(姓名+身份证号是否真实)
const verifyResult = await verifyIdCard(name, idCard);
if (!verifyResult.success) return fail('身份信息核验未通过');
// 步骤4:活体检测(采集人脸视频,与身份证照片比对)
const faceResult = await faceApi.livenessCheck({
video: faceVideo,
idCardImage: ocrResult.data.portrait, // 身份证上的照片
livenessType: 'action', // 动作活体
actions: ['blink', 'open_mouth', 'turn_head']
});
if (faceResult.score < 0.8) {
return fail('人脸验证未通过,请重试');
}
// 步骤5:全部通过,保存认证状态(脱敏存储)
await db.update('user_verify', {
user_id: currentUserId,
real_name: maskName(name), // 张*三
id_card: maskIdCard(idCard), // 4101**********1234
verify_status: 'verified',
verify_time: now(),
verify_method: 'face'
});
return success('实名认证成功');
}
活体检测的安全性在于"防翻拍"。单纯照片翻拍骗不过动作活体,但高清晰度视频翻拍可能蒙混过关。金融级场景建议用"数字活体"(屏幕随机数字,用户读出来),安全性更高但体验略重。尧图在医疗项目里用动作活体 + 人脸与证件照相似度阈值 0.8 的组合,既保证安全又不过度打扰用户。
三、敏感数据存储与合规
实名认证必然涉及身份证号、人脸照片等敏感信息,存储不当就是数据安全事故。尧图遵循三条原则:能不存就不存、必须存要加密、展示时必须脱敏。身份证号只存脱敏后的(如 4101**********1234),完整号码若必须留存则用 AES 加密存库;人脸照片认证完即删,只留认证结果状态。
// 敏感数据处理
function maskName(name) {
// 张三 → 张*,欧阳娜娜 → 欧***娜
if (name.length <= 2) return name[0] + '*';
return name[0] + '*'.repeat(name.length - 2) + name[name.length - 1];
}
function maskIdCard(idCard) {
// 410102199001011234 → 4101**********1234
return idCard.slice(0, 4) + '*'.repeat(10) + idCard.slice(-4);
}
// 完整身份证号加密存储(如法规要求留存)
const crypto = require('crypto');
function encryptIdCard(idCard) {
const cipher = crypto.createCipheriv('aes-256-gcm',
SECRET_KEY, IV);
let encrypted = cipher.update(idCard, 'utf8', 'hex');
encrypted += cipher.final('hex');
return encrypted; // 仅在司法调证时解密使用
}
合规层面,实名认证前必须获得用户明示授权——《个人信息保护法》要求采集敏感信息前单独告知用途并取得同意。尧图会在认证页面设置独立勾选的授权协议,记录授权时间和 IP 作为凭证。另外,认证频率要限制(如 24 小时内最多 3 次),防止有人拿接口当试错工具暴力撞库。把安全、合规、体验三者平衡好,实名认证才能既满足监管又不劝退用户。