You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 23:06:09