WebAuthn适配教程

本文共计 2968 个字,预计阅读时长 10 分钟

近期给教务系统适配了Passkeys,出一篇教程如何使用和适配。

1. 什么是 Passkeys?

Passkeys 是基于 WebAuthn(Web Authentication API)的新一代身份验证方式,可以取代传统密码登录。
它的核心特点是:

  • 免密码:用户无需记忆复杂密码
  • 更安全:公钥加密,防钓鱼、防数据库泄露
  • 跨设备同步:iCloud Keychain、Google Password Manager 等可自动同步
  • 无感体验:生物识别(指纹/人脸)或设备 PIN 码验证

简单来说,Passkeys 让用户只需用指纹人脸就能安全登录你的应用。

2. WebAuthn 工作流程

  1. 注册阶段
    • 服务器生成注册挑战(challenge)
    • 前端调用 navigator.credentials.create() 创建凭证
    • 客户端返回公钥信息给服务器存储
  2. 登录阶段
    • 服务器生成登录挑战
    • 前端调用 navigator.credentials.get() 验证凭证
    • 客户端签名返回给服务器进行验证

3. 浏览器支持情况

Passkeys 依赖 WebAuthn Level 2 API,主流浏览器(Chrome、Edge、Safari、Firefox)在桌面和移动端都已支持。

可以用下面代码检测支持情况:

<script> if (window.PublicKeyCredential) { console.log("✅ 支持 WebAuthn API"); } else { console.log("❌ 当前环境不支持 WebAuthn"); } </script>

4. 注册 Passkey 示例

下面的例子模拟了前端调用注册 Passkey 的流程。
实际生产环境中,challenge 和 RP ID 需要从服务器获取。

<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Passkeys 注册示例</title> </head> <body> <button id="registerBtn">注册 Passkey</button> <script> // Base64URL 转 ArrayBuffer function base64urlToArrayBuffer(base64url) { let base64 = base64url.replace(/-/g, '+').replace(/_/g, '/'); let binary = atob(base64); let bytes = new Uint8Array(binary.length); for (let i = 0; i < binary.length; i++) { bytes[i] = binary.charCodeAt(i); } return bytes.buffer; } document.getElementById('registerBtn').addEventListener('click', async () => { // 模拟后端返回的注册参数 const publicKey = { challenge: Uint8Array.from(window.crypto.getRandomValues(new Uint8Array(32))), rp: { name: "My Website", id: window.location.hostname }, user: { id: Uint8Array.from(window.crypto.getRandomValues(new Uint8Array(16))), name: "testuser@example.com", displayName: "Test User" }, pubKeyCredParams: [{ type: "public-key", alg: -7 }], authenticatorSelection: { authenticatorAttachment: "platform", residentKey: "required", userVerification: "preferred" }, timeout: 60000, attestation: "direct" }; try { const credential = await navigator.credentials.create({ publicKey }); console.log("注册成功", credential); // 发送 credential 到服务器保存 // fetch("/register", { method: "POST", body: JSON.stringify(credential) }); } catch (err) { console.error("注册失败", err); } }); </script> </body> </html>

5. 登录 Passkey 示例

<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Passkeys 登录示例</title> </head> <body> <button id="loginBtn">使用 Passkey 登录</button> <script> document.getElementById('loginBtn').addEventListener('click', async () => { // 模拟后端返回 challenge const publicKey = { challenge: Uint8Array.from(window.crypto.getRandomValues(new Uint8Array(32))), allowCredentials: [{ type: "public-key", id: Uint8Array.from(window.crypto.getRandomValues(new Uint8Array(16))) }], timeout: 60000, userVerification: "preferred" }; try { const assertion = await navigator.credentials.get({ publicKey }); console.log("登录成功", assertion); // 将 assertion 发送到服务器验证 // fetch("/login", { method: "POST", body: JSON.stringify(assertion) }); } catch (err) { console.error("登录失败", err); } }); </script> </body> </html>

6. 开发注意事项

  • HTTPS 必须:WebAuthn 只能在 HTTPS 或 localhost 下运行
  • 服务器参与:实际 challenge 和 credential ID 必须由后端生成和验证
  • 跨设备同步:如果用户用的是 iCloud Keychain/Google Password Manager,同一账号设备可以直接登录
  • UI 设计:给用户明确提示,比如“使用 Face ID 登录”或“使用 Passkey 登录”

7. 总结

Passkeys + WebAuthn 是未来主流的身份认证方式,相比传统密码安全性更高、体验更好。
前端实现主要分为两步:

  1. navigator.credentials.create() 注册
  2. navigator.credentials.get() 登录

配合后端 API,即可轻松实现无密码的登录体验。