web凭据无密码怎么添加
我上周给客户的据无内部系统加无密码登录,折腾了整整两天。密码问题不在技术,添加而在概念混淆——他们把“无密码”和“免登录”当成一回事了。据无实际上,密码Web无密码凭据(WebAuthn)要解决的添加是“不用输入密码也能证明是你”,而不是据无“跳过认证直接进”。搞清楚这点,密码添加流程其实很直接。添加
先看最核心的据无一步:你得有一个支持WebAuthn的服务器端点。别急着写前端,密码先把后端接口定了。添加这个接口要能处理两件事——注册挑战(registration challenge)和断言挑战(assertion challenge)。据无简单说,密码注册时服务器生成一段随机字符串,添加客户端用安全密钥(比如YubiKey或手机内置的指纹模块)对它签名,服务器验证签名后,把公钥存下来。下次登录,服务器再发一段新挑战,客户端用同一个私钥签名,服务器比对公钥,通过就放行。
这里有个坑,我差点栽进去。很多教程让你直接用浏览器的navigator.credentials.create(),但没提醒你——这个API只能在HTTPS环境下跑,而且必须用用户手势触发(比如点击按钮)。我一开始在页面加载时自动调用,结果Chrome直接报错,控制台提示“user mediation required”。后来改成点击“注册安全密钥”按钮才触发,问题解决。
前端代码其实不长,核心就两段。注册时:
const publicKey = { challenge: new Uint8Array(32), // 从服务器获取 rp: { name: "我的系统" }, user: { id: new Uint8Array(16), name: "user@example.com", displayName: "张三" }, pubKeyCredParams: [{ alg: -7, type: "public-key" }] }; const credential = await navigator.credentials.create({ publicKey }); 注意challenge别自己瞎编,一定要从服务器拿,否则浏览器会拒绝。还有user.id是固定不变的,别每次登录都生成新的,否则服务器端没法关联用户。
登录时的断言更简单,但有个细节容易漏:allowCredentials参数。如果你不指定,浏览器会弹出所有已注册的密钥让用户选,体验很糟。正确做法是从数据库查出该用户绑定的凭据ID,填进去,这样用户只需碰一下密钥就能过。
服务器端验证是重头戏。别自己实现加密算法,用现成库。Node.js推荐@simplewebauthn/server,Python用py_webauthn。这些库会处理签名验证、挑战时效(通常5分钟过期)、反重放攻击。我见过有人手写验证逻辑,结果把RSA签名算法搞错,导致所有安卓手机登录失败——安卓的密钥算法和苹果不一样,默认用ES256,但有些老设备会回退到RS256,你得在注册时同时支持两种算法。
存储方面,别把公钥明文丢在数据库里。至少要做哈希处理,或者用加密列。更关键的是,每个用户可能绑定多个密钥(手机+电脑+YubiKey),你得建一张关联表,记录凭据ID、公钥、签名计数器和最后使用时间。签名计数器是防克隆用的,每次登录后要更新,如果发现新值小于旧值,说明密钥被复制了,要报警。
最后说个冷门但实用的点。WebAuthn支持“可发现凭据”(discoverable credentials),意思是用户不用先输入用户名,直接碰一下密钥就能登录。这体验极好,但需要额外设置residentKey: "required",而且服务器要支持根据凭据ID反查用户。我建议第一版先别做这个,因为调试起来很麻烦——浏览器对resident key的支持在不同系统上表现不一致,Windows Hello没问题,但某些Linux桌面环境会直接报错。
整体流程跑通后,你会发现无密码登录其实不复杂。麻烦的是兼容性测试:Chrome、Edge、Safari基本没问题,Firefox对WebAuthn的支持有点滞后,部分旧版本需要手动开启flag。还有iOS 14.5以下的Safari不支持平台认证器,只能外接安全密钥。我的经验是,先支持主流浏览器,再慢慢补兼容层,别一开始就想全平台覆盖。
动手吧。把后端接口写好,前端调通,再处理边界情况——比如用户丢失密钥后的恢复流程。那才是真正的难点。
