20241205 免密码登录之Passkey 使用指纹登录
为了简化登录,给自己偷懒,就有了如下的方案
案例代码
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>WebAuthn Passkey Login Demo (前端模拟指纹登录)</title>
</head>
<body>
<button id="registerButton">注册 Passkey(模拟指纹注册)</button>
<button id="loginButton">使用 Passkey 登录(模拟指纹登录)</button>
<script>
// 获取页面上的注册和登录按钮元素
const registerButton = document.getElementById('registerButton');
const loginButton = document.getElementById('loginButton');
// 模拟存储已注册用户信息(仅前端模拟,实际应存储在后端)
const registeredUsers = {};
// 注册 Passkey 流程(模拟指纹注册)
registerButton.addEventListener('click', async () => {
try {
const publicKeyCredentialCreationOptions = {
// 依赖方(示例网站)相关信息
rp: {
name: "Your Website",
id: "www.guagau0127.fun"
},
// 用户信息
user: {
id: new Uint8Array(16), // 简单示例,实际应唯一标识用户,如哈希后的用户ID
name: "demo_user",
displayName: "Demo User"
},
// 挑战值,这里简单模拟生成,实际应由后端安全生成传递过来
challenge: Uint8Array.from(window.crypto.getRandomValues(new Uint8Array(32))),
pubKeyCredParams: [
{
type: "public-key",
alg: -7 // ES256算法对应的编号
},
{
type: "public-key",
alg: -257 // RS256算法对应的编号,添加此项以满足要求
}
],
// 认证器选择配置,优先使用生物识别(如指纹)验证
authenticatorSelection: {
authenticatorAttachment: "platform",
requireResidentKey: false,
userVerification: "preferred"
}
};
const credential = await navigator.credentials.create({
publicKey: publicKeyCredentialCreationOptions
});
console.log('注册成功,凭证信息: ', credential);
// 模拟将凭证信息与用户关联存储(仅前端模拟,实际应发送到后端存储)
const { id } = credential;
registeredUsers[id] = credential;
} catch (error) {
console.error('注册凭证失败: ', error);
}
});
// 使用 Passkey 登录流程(模拟指纹登录)
loginButton.addEventListener('click', async () => {
try {
const publicKeyCredentialRequestOptions = {
challenge: Uint8Array.from(window.crypto.getRandomValues(new Uint8Array(32))), // 新的挑战值,模拟生成,实际后端生成
allowCredentials: Object.values(registeredUsers).map(user => ({
type: "public-key",
id: user.id.buffer // 修改此处,获取正确的ArrayBuffer类型数据作为id的值
})),
userVerification: "preferred"
};
const assertion = await navigator.credentials.get({
publicKey: publicKeyCredentialRequestOptions
});
console.log('登录成功,断言信息: ', assertion);
// 这里省略了发送到后端验证的步骤,仅前端模拟登录成功提示
alert('登录成功(模拟,实际需后端验证)');
} catch (error) {
console.error('登录失败: ', error);
}
});
</script>
</body>
</html>