admin管理员组

文章数量:1036093

ai共同开发

最近在开发一个基于 JWT(JSON Web Token)的身份验证功能时,我遇到了一个让人头疼的问题:解码后的 JWT 中的中文字符显示为乱码。经过一番折腾,在deepseek上终于找到了解决方案。


问题背景

JWT 是一种用于在网络应用之间安全传递信息的开放标准(RFC 7519)。它由三部分组成:

  1. Header(头部):描述签名算法和令牌类型。
  2. Payload(载荷):包含实际的用户信息(如用户 ID、角色、权限等)。
  3. Signature(签名):用于验证令牌的完整性。

在我的项目中,JWT 的 Payload 部分包含了一些中文字符(如公司名称)。然而,解码后这些中文字符却变成了乱码,例如:

代码语言:json复制
"X-Access-Control-Header-Company-Name": "æ¨ªæ¸ æœºç”µç§‘æŠ€æœ‰é™å…¬"

把问题抛给deepseek,问了一下是什么问题。


问题分析

1. Base64Url 编码问题

JWT 的 HeaderPayload 部分是使用 Base64Url 编码的。Base64Url 是 Base64 的一种变体,主要区别在于:

  • 使用 -_ 替代了 Base64 中的 +/
  • 去掉末尾的 = 填充符。
2. atob 函数的局限性

JavaScript 的 atob 函数用于解码 Base64 字符串,但它只能处理 ASCII 字符。如果字符串中包含非 ASCII 字符(如中文),解码结果就会变成乱码。

3. UTF-8 编码问题

中文字符在 JWT 中是使用 UTF-8 编码的,而 atob 并不支持 UTF-8 解码,因此需要额外的处理。


解决方案

为了解决这个问题,我对 decodeJWT 方法进行了改进,主要步骤如下:

1. Base64Url 解码

将 Base64Url 转换为标准的 Base64,并补充末尾的 = 填充符:

代码语言:javascript代码运行次数:0运行复制
let base64 = str.replace(/-/g, '+').replace(/_/g, '/');
const padLength = 4 - (base64.length % 4);
if (padLength < 4) {
  base64 += '='.repeat(padLength);
}
2. UTF-8 解码

将解码后的 Base64 字符串转换为 UTF-8 编码的字符串:

代码语言:javascript代码运行次数:0运行复制
const utf8Str = decodeURIComponent(
  atob(base64)
    .split('')
    .map((c) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
    .join('')
);
3. 完整代码

以下是改进后的 decodeJWT 方法:

代码语言:javascript代码运行次数:0运行复制
function decodeJWT(jwt) {
  const parts = jwt.split('.');
  if (parts.length !== 3) {
    throw new Error('Invalid JWT');
  }

  const base64UrlDecode = (str) => {
    let base64 = str.replace(/-/g, '+').replace(/_/g, '/');
    const padLength = 4 - (base64.length % 4);
    if (padLength < 4) {
      base64 += '='.repeat(padLength);
    }
    return decodeURIComponent(
      atob(base64)
        .split('')
        .map((c) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
        .join('')
    );
  };

  const header = JSON.parse(base64UrlDecode(parts[0]));
  const payload = JSON.parse(base64UrlDecode(parts[1]));

  return [header, payload, parts[2]];
}

测试与验证

使用改进后的方法解码 JWT,结果如下:

输入 JWT
代码语言:json复制
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJYLUFjY2Vzcy1Db250cm9sLUhlYWRlci1Db21wYW55LU5hbWUiOiLmqKrmuKDmnLrnlLXnp5HmioDmnInpmZDlhawiLCJyb2xlcyI6WyJST0xFX01BTkFHRVIiXSwiYXV0aG9yaXRpZXMiOlsiUFJPSkVDVF9ERUxFVEUiLCJQUk9KRUNUX1ZJRVciLCJQUk9KRUNUX0NSRUFURSIsIlBST0pFQ1RfSU5BQ1RJVkUiLCJQUk9KRUNUX0FDVElWRSIsIlBST0pFQ1RfRURJVCIsIlBST0pFQ1RfQVNTSUdOIiwiU1RBVElPTl9WSUVXIiwiU1RBVElPTl9DUkVBVEUiLCJTVEFUSU9OX0lOQUNUSVZFIiwiU1RBVElPTl9BQ1RJVkUiLCJTVEFUSU9OX0VESVQiLCJERVZJQ0VfVFlQRV9WSUVXIiwiREVWSUNFX1RZUEVfQ1JFQVRFIiwiREVWSUNFX1RZUEVfREVMRVRFIiwiREVWSUNFX1RZUEVfRURJVCIsIkRFVklDRV9WSUVXIiwiREVWSUNFX0NSRUFURSIsIkRFVklDRV9JTkFDVElWRSIsIkRFVklDRV9BQ1RJVkUiLCJERVZJQ0VfRURJVCIsIkZJTEVfVVBMT0FEIiwiRklMRV9WSUVXIiwiVElDS0VUX1ZJRVciLCJUSUNLRVRfQ1JFQVRFIiwiVElDS0VUX1VQREFURSJdLCJzdWIiOiIxMjM0NTY3OCIsImV4cCI6MTc0MjMwNzQwNn0.R6cEpWzIquvwyBcOVYMtatMoVSj-0MuhDJ6Q1qLzenM
输出结果
代码语言:json复制
Header: {
  "alg": "HS256",
  "typ": "JWT"
}

Payload: {
  "X-Access-Control-Header-Company-Name": "某某公司",
  "roles": ["ROLE_MANAGER"],
  "authorities": [
    "PROJECT_DELETE",
    "PROJECT_VIEW",
    "PROJECT_CREATE",
    "PROJECT_INACTIVE",
    "PROJECT_ACTIVE",
    "PROJECT_EDIT",
    "PROJECT_ASSIGN",
    "STATION_VIEW",
    "STATION_CREATE",
    "STATION_INACTIVE",
    "STATION_ACTIVE",
    "STATION_EDIT",
    "DEVICE_TYPE_VIEW",
    "DEVICE_TYPE_CREATE",
    "DEVICE_TYPE_DELETE",
    "DEVICE_TYPE_EDIT",
    "DEVICE_VIEW",
    "DEVICE_CREATE",
    "DEVICE_INACTIVE",
    "DEVICE_ACTIVE",
    "DEVICE_EDIT",
    "FILE_UPLOAD",
    "FILE_VIEW",
    "TICKET_VIEW",
    "TICKET_CREATE",
    "TICKET_UPDATE"
  ],
  "sub": "12345678",
  "exp": 1742307406
}

Signature: "R6cEpWzIquvwyBcOVYMtatMoVSj-0MuhDJ6Q1qLzenM"

希望这篇文章能帮助你解决类似的问题!如果你有其他问题或更好的解决方案,欢迎在评论区分享!

ai共同开发

最近在开发一个基于 JWT(JSON Web Token)的身份验证功能时,我遇到了一个让人头疼的问题:解码后的 JWT 中的中文字符显示为乱码。经过一番折腾,在deepseek上终于找到了解决方案。


问题背景

JWT 是一种用于在网络应用之间安全传递信息的开放标准(RFC 7519)。它由三部分组成:

  1. Header(头部):描述签名算法和令牌类型。
  2. Payload(载荷):包含实际的用户信息(如用户 ID、角色、权限等)。
  3. Signature(签名):用于验证令牌的完整性。

在我的项目中,JWT 的 Payload 部分包含了一些中文字符(如公司名称)。然而,解码后这些中文字符却变成了乱码,例如:

代码语言:json复制
"X-Access-Control-Header-Company-Name": "æ¨ªæ¸ æœºç”µç§‘æŠ€æœ‰é™å…¬"

把问题抛给deepseek,问了一下是什么问题。


问题分析

1. Base64Url 编码问题

JWT 的 HeaderPayload 部分是使用 Base64Url 编码的。Base64Url 是 Base64 的一种变体,主要区别在于:

  • 使用 -_ 替代了 Base64 中的 +/
  • 去掉末尾的 = 填充符。
2. atob 函数的局限性

JavaScript 的 atob 函数用于解码 Base64 字符串,但它只能处理 ASCII 字符。如果字符串中包含非 ASCII 字符(如中文),解码结果就会变成乱码。

3. UTF-8 编码问题

中文字符在 JWT 中是使用 UTF-8 编码的,而 atob 并不支持 UTF-8 解码,因此需要额外的处理。


解决方案

为了解决这个问题,我对 decodeJWT 方法进行了改进,主要步骤如下:

1. Base64Url 解码

将 Base64Url 转换为标准的 Base64,并补充末尾的 = 填充符:

代码语言:javascript代码运行次数:0运行复制
let base64 = str.replace(/-/g, '+').replace(/_/g, '/');
const padLength = 4 - (base64.length % 4);
if (padLength < 4) {
  base64 += '='.repeat(padLength);
}
2. UTF-8 解码

将解码后的 Base64 字符串转换为 UTF-8 编码的字符串:

代码语言:javascript代码运行次数:0运行复制
const utf8Str = decodeURIComponent(
  atob(base64)
    .split('')
    .map((c) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
    .join('')
);
3. 完整代码

以下是改进后的 decodeJWT 方法:

代码语言:javascript代码运行次数:0运行复制
function decodeJWT(jwt) {
  const parts = jwt.split('.');
  if (parts.length !== 3) {
    throw new Error('Invalid JWT');
  }

  const base64UrlDecode = (str) => {
    let base64 = str.replace(/-/g, '+').replace(/_/g, '/');
    const padLength = 4 - (base64.length % 4);
    if (padLength < 4) {
      base64 += '='.repeat(padLength);
    }
    return decodeURIComponent(
      atob(base64)
        .split('')
        .map((c) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
        .join('')
    );
  };

  const header = JSON.parse(base64UrlDecode(parts[0]));
  const payload = JSON.parse(base64UrlDecode(parts[1]));

  return [header, payload, parts[2]];
}

测试与验证

使用改进后的方法解码 JWT,结果如下:

输入 JWT
代码语言:json复制
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJYLUFjY2Vzcy1Db250cm9sLUhlYWRlci1Db21wYW55LU5hbWUiOiLmqKrmuKDmnLrnlLXnp5HmioDmnInpmZDlhawiLCJyb2xlcyI6WyJST0xFX01BTkFHRVIiXSwiYXV0aG9yaXRpZXMiOlsiUFJPSkVDVF9ERUxFVEUiLCJQUk9KRUNUX1ZJRVciLCJQUk9KRUNUX0NSRUFURSIsIlBST0pFQ1RfSU5BQ1RJVkUiLCJQUk9KRUNUX0FDVElWRSIsIlBST0pFQ1RfRURJVCIsIlBST0pFQ1RfQVNTSUdOIiwiU1RBVElPTl9WSUVXIiwiU1RBVElPTl9DUkVBVEUiLCJTVEFUSU9OX0lOQUNUSVZFIiwiU1RBVElPTl9BQ1RJVkUiLCJTVEFUSU9OX0VESVQiLCJERVZJQ0VfVFlQRV9WSUVXIiwiREVWSUNFX1RZUEVfQ1JFQVRFIiwiREVWSUNFX1RZUEVfREVMRVRFIiwiREVWSUNFX1RZUEVfRURJVCIsIkRFVklDRV9WSUVXIiwiREVWSUNFX0NSRUFURSIsIkRFVklDRV9JTkFDVElWRSIsIkRFVklDRV9BQ1RJVkUiLCJERVZJQ0VfRURJVCIsIkZJTEVfVVBMT0FEIiwiRklMRV9WSUVXIiwiVElDS0VUX1ZJRVciLCJUSUNLRVRfQ1JFQVRFIiwiVElDS0VUX1VQREFURSJdLCJzdWIiOiIxMjM0NTY3OCIsImV4cCI6MTc0MjMwNzQwNn0.R6cEpWzIquvwyBcOVYMtatMoVSj-0MuhDJ6Q1qLzenM
输出结果
代码语言:json复制
Header: {
  "alg": "HS256",
  "typ": "JWT"
}

Payload: {
  "X-Access-Control-Header-Company-Name": "某某公司",
  "roles": ["ROLE_MANAGER"],
  "authorities": [
    "PROJECT_DELETE",
    "PROJECT_VIEW",
    "PROJECT_CREATE",
    "PROJECT_INACTIVE",
    "PROJECT_ACTIVE",
    "PROJECT_EDIT",
    "PROJECT_ASSIGN",
    "STATION_VIEW",
    "STATION_CREATE",
    "STATION_INACTIVE",
    "STATION_ACTIVE",
    "STATION_EDIT",
    "DEVICE_TYPE_VIEW",
    "DEVICE_TYPE_CREATE",
    "DEVICE_TYPE_DELETE",
    "DEVICE_TYPE_EDIT",
    "DEVICE_VIEW",
    "DEVICE_CREATE",
    "DEVICE_INACTIVE",
    "DEVICE_ACTIVE",
    "DEVICE_EDIT",
    "FILE_UPLOAD",
    "FILE_VIEW",
    "TICKET_VIEW",
    "TICKET_CREATE",
    "TICKET_UPDATE"
  ],
  "sub": "12345678",
  "exp": 1742307406
}

Signature: "R6cEpWzIquvwyBcOVYMtatMoVSj-0MuhDJ6Q1qLzenM"

希望这篇文章能帮助你解决类似的问题!如果你有其他问题或更好的解决方案,欢迎在评论区分享!

本文标签: ai共同开发