使用Web Crypto API验证JWT始终返回false的问题排查
问题分析与修复
你的验证函数始终返回false有两个关键问题,以下是对应的修复方案:
核心问题1:待验证内容拼接错误
JWT的签名是基于header.payload(两个部分用英文句号.连接)生成的,但你的代码中使用user.split(".").splice(0, 2).join()时没有添加分隔符,导致生成的字符串是headerpayload(无分隔符),和签名时的原始数据完全不符,这是验证失败的主要原因。
核心问题2:可选优化:避免重复拆分JWT
你的代码重复执行了user.split("."),提前提取变量可以提升可读性和性能。
修复后的代码
async function verify(user: string) { // 提前拆分JWT的三个部分,同时做格式校验 const [header, payload, signatureB64Url] = user.split("."); if (!header || !payload || !signatureB64Url) { throw new Error("Invalid JWT format"); } const secret = await crypto.subtle.importKey( "raw", new TextEncoder().encode(process.env.SECRET!), { name: "HMAC", hash: { name: "SHA-256" }, }, false, ["verify"] ); // 拼接正确的待验证内容:header.payload const dataToVerify = new TextEncoder().encode(`${header}.${payload}`); // 解码base64url格式的签名 const signature = Buffer.from(signatureB64Url, "base64url"); return crypto.subtle.verify( "HMAC", secret, signature, dataToVerify ); }
纯浏览器环境适配说明
如果是在纯浏览器环境(而非Node.js),Buffer不可用,需要替换为base64url转ArrayBuffer的实现:
function base64UrlToArrayBuffer(base64Url: string): ArrayBuffer { // 替换base64url的特殊字符为标准base64格式 const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/"); // 补全base64所需的padding const padded = base64.padEnd(base64.length + ((4 - base64.length % 4) % 4), "="); const binaryStr = atob(padded); const arrayBuffer = new ArrayBuffer(binaryStr.length); const uint8Array = new Uint8Array(arrayBuffer); for (let i = 0; i < binaryStr.length; i++) { uint8Array[i] = binaryStr.charCodeAt(i); } return arrayBuffer; }
之后将const signature = Buffer.from(...)替换为const signature = base64UrlToArrayBuffer(signatureB64Url)即可。
内容的提问来源于stack exchange,提问作者muell
相关产品推荐
相关产品推荐

