在JavaScript中手动验证GCP JWT签名失败,如何解决?
核心问题点
你代码里有2个核心错误导致验证始终失败:
- 用于签名校验的明文构造错误:RS256签名的校验对象是JWT原始的
[base64Url(header)].[base64Url(payload)]字符串,不是你把header、payload解码后重新JSON序列化拼接的内容。JSON序列化时键的顺序、空格缩进都会导致最终的字符串和原始内容不一致,自然校验失败。 - 签名值的解码错误:JWT第三段的signature是Base64Url编码后的二进制值,你直接用
TextEncoder.encode()把它当成普通UTF8字符串转字节数组完全错误,需要先将Base64Url格式的签名解码为原始二进制字节流。
修正方案
你只需要调整decodeJWT返回值和byteArrays的构造逻辑即可,所有API都是浏览器原生实现,不需要依赖Node核心模块:
function decodeJWT(jwtString: string) { const matchRes = jwtString.match( /(?<headerRaw>[^.]+)\.(?<payloadRaw>[^.]+)\.(?<signatureRaw>[^.]+)/ ).groups; // 保留原始Base64Url段用于校验,同时解析内容供业务使用 return { headerRaw: matchRes.headerRaw, payloadRaw: matchRes.payloadRaw, signatureRaw: matchRes.signatureRaw, header: JSON.parse(urlBase64toStr(matchRes.headerRaw)), payload: JSON.parse(urlBase64toStr(matchRes.payloadRaw)), }; } // Base64Url转原始二进制数组的通用方法 function base64UrlToUint8Array(base64Url: string): Uint8Array { // 转换为标准Base64格式 const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/'); // 补全填充等号 const padLength = (4 - (base64.length % 4)) % 4; const paddedBase64 = base64.padEnd(base64.length + padLength, '='); // 转二进制字节 const binaryStr = atob(paddedBase64); const uint8Arr = new Uint8Array(binaryStr.length); for (let i = 0; i < binaryStr.length; i++) { uint8Arr[i] = binaryStr.charCodeAt(i); } return uint8Arr; } const jwt = decodeJWT('<....JWT string here......>') const encoder = new TextEncoder(); const byteArrays = { signature: base64UrlToUint8Array(jwt.signatureRaw), body: encoder.encode(`${jwt.headerRaw}.${jwt.payloadRaw}`) };
调整后你之前使用jose、jsrsasign的两段验证代码都可以正常返回true。
内容的提问来源于stack exchange,提问作者David Min
相关产品推荐
相关产品推荐

