HandyTools Hub

🔏 JWT 编码器

在本地创建并签名 HS256 JSON Web Token。

🔒 您的数据绝不会离开浏览器

⚠️ 仅供测试——切勿把生产环境的签名密钥粘贴到任何在线工具中。
算法: HS256

在浏览器中直接创建已签名的 JSON Web Token。编辑 header 和 payload JSON,输入密钥,即可获得使用 WebCrypto 的 HMAC-SHA256 在本地签名的、可直接使用的 HS256 令牌,数据绝不离开你的设备。它面向测试需要认证的 API、复现与令牌相关的问题,以及学习 JWT 的构造方式。

签名是如何工作的

一个 JWT 由点分隔的三段 base64url 组成:header.payload.signature。编码器执行以下步骤:

  1. 把 header 和 payload 解析为 JSON,任一无效即快速失败。
  2. 将每个 JSON 字符串按原样做 base64url 编码(去掉填充,+ → -,/ → _)。
  3. 计算 signature = HMAC-SHA256(header + "." + payload, secret),并对原始字节做 base64url 编码。

结果就是令牌。每一段都使用 base64url,它区分大小写并去掉末尾的 = 填充。验证是同样的操作反过来做:服务器用自己那份密钥重新计算 HMAC 并比对。载荷哪怕只改一个字符,签名就对不上。

分步操作

  1. 编辑 header——默认的 {"alg": "HS256", "typ": "JWT"} 一般够用,除非需要额外的头部字段,如 kid。
  2. 编辑 payload——添加 sub、name、iat、exp、roles 等声明。值必须是合法 JSON。
  3. 输入密钥——键入共享的 HMAC 密钥。使用一次性测试值,绝不要用生产密钥。
  4. 读取令牌——字段随输入实时重新签名,Sign 按钮可强制重跑一次。
  5. 复制——一键复制成品令牌。

实操示例:复现一个已知令牌

Header(紧凑形式,无空格):

{"alg":"HS256","typ":"JWT"}

Payload:

{"sub":"1234567890","name":"John Doe","iat":1516239022}

密钥:your-256-bit-secret。编码后的各段为:

  • header → eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
  • payload → eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
  • signature → SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

得到完整令牌:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

把密钥改动一个字符,签名段会完全变化,而前两段保持不变。这正是关键所在:载荷是公开的,签名是证明。

签名不是加密

这是 JWT 最常被误解的一点。HS256 提供的是完整性和真实性,而非机密性。用 JWT 解码器解码上面的令牌,声明会以明文出现——base64url 是编码,不是密码。任何截获令牌的人也能在它过期前重放,因此:

  • 载荷中只放非敏感声明:用户 id、角色、作用域、过期时间。
  • 密钥、密码和个人数据保留在服务端。
  • 使用 TLS,使令牌无法在传输中被窃取。
  • 在信任任何内容前,先在服务器上验证签名和 exp 声明。

工具本身也无法让弱密钥变安全。泄露或可猜到的 HMAC 密钥会让任何人铸造有效令牌,这也是本页仅供开发使用的原因。

常见错误

  • 粘贴生产密钥。 用测试密钥签测试令牌,并轮换任何曾被输入浏览器工具的密钥。
  • 忘记空白也有影响。 文本域中的默认 header 含换行和缩进,因此它的签名与上面的紧凑形式不同,即便 JSON 语义完全相同。
  • 期待声明校验。 exp 已过期、nbf 在未来,或把 role 拼错,都会毫无怨言地完成签名。校验是服务器的职责。
  • 在互不信任的各方之间使用 HS256。 对称密钥必须在每个验证方那里都保密。若多个服务需要验证,优先使用非对称算法。
  • 把令牌当成加密的。 见上文——假定载荷可读。

HS256 与 RS256

HS256 是对称的:一个密钥既签名又验证,因此每个验证服务也必须能签名,其中任何一处泄露都会危及全部。RS256 和 ES256 是非对称的:私钥签名,公钥验证。这种分离让 API 网关和资源服务器无需持有签名能力就能验证令牌,这也是公钥算法主导多服务架构的原因。

本编码器只支持 HS256,因为浏览器测试无需管理私钥。如果你的目标系统使用 RS256,请用你自己的工具铸造令牌,再用 JWT 解码器确认结构与声明——解码出的 header 会显示 "alg": "RS256"。

在浏览器之外验证令牌

不使用任何 JWT 库也能复现签名。一段使用内置 crypto 模块的简短 Node 脚本,做的正是服务器每次请求时做的事:

const crypto = require('crypto');
const [h, p, sig] = token.split('.');
const expected = crypto.createHmac('sha256', secret)
  .update(`${h}.${p}`).digest('base64url');
console.log(expected === sig); // 仅当密钥匹配时为 true

Node 和现代浏览器都提供 base64url,因此无需手动替换 +//。如果比较结果为 false,说明令牌是用不同密钥签的、载荷被改动过,或 header 与 payload 字符串被以不同空白重新序列化。直接拒绝它即可。

相关工具

常见问题

这个 JWT 编码器支持哪种签名算法?

它使用 HS256(HMAC-SHA256)签名,这是部署最广的对称 JWT 算法,借助浏览器原生 WebCrypto API 完成。同一个密钥字符串既用于签名,也用于后续验证。RS256 这类非对称算法需要公钥/私钥对,这里不支持。

JWT 是加密的还是签名的?

是签名的,不是加密的。HS256 保证令牌由持有密钥的人创建、且载荷未被篡改,但头部和载荷只做了 base64url 编码——任何人都能解码阅读。切勿把密码、API 密钥或个人数据放进 JWT 载荷。

header 和 payload 应该是什么样子?

两个字段都期望合法的 JSON。header 通常是 {"alg": "HS256", "typ": "JWT"},payload 携带你的声明,例如 sub、name、iat 和 exp。工具预载了可直接编辑的可用示例;JSON 无效会停止签名并显示错误。

这个工具会校验我的声明或过期时间吗?

不会。它严格按你输入的内容签名,不检查 exp 是否已过期、nbf 是否在未来,也不检查声明名是否符合注册声明约定。若要只查看成品令牌而不验证它,请使用 JWT 解码器。

为什么我的令牌与其他编码器的输出不同?

签名覆盖编码后 header 和 payload 的精确字节,而编码器只去除首尾空白、保留 JSON 内部的一切。冒号后的一个空格、一个换行或不同的键顺序都会改变 base64url 字符串,从而改变签名——它们仍然都可以是有效令牌。

HS256 的密钥应该多长?

至少 32 字节(256 位)随机数据,与 SHA-256 的输出长度匹配。WebCrypto 接受任意长度的字符串,所以本工具用短口令也能签名,但短密钥可被离线暴力破解。真实用途请生成足够长的随机密钥。

由 HandyTools Hub 团队审校 · 最后更新