浅谈 JWT (JSON Web Tokens)
本文假定您有基础的 Web 知识,不会对某些概念进行说明。
JWT 是什么?
JWT,全称是 JSON Web Token,可是说是当下最流行的一种跨域的认证方案。
1 | eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJHYXZpbiIsImlhdCI6MTc3NDk3MDgyMywiZXhwIjoxNzc0OTc0NDIzfQ.M1tneo2EbPuE5QLdDkGNJk0V9QQ2-ZOXYyRqqID5HH3mUqec4DHQsinG5F_ovDdKlsNeyg1vDnN5vDMf_tEocHIHiBCsfX14YuCWb6lRZ9prJkbfJHXwqGu94yPskGo5dfX7_D_lmNznAOloFK_0GEvhpNmIBrwe9bw0drRRTH8BPKmR_xscCSKT-mQxfbgXGSfKohxPnjhl9PZVnXe5q_2D5cVu3PIr6yeOmbAI-Ju8C1U9cC0DqM4P8m53WktbjyxW2k8uSIOvVOnaNIP7y5CMTmC-pG4JYMzHCxn5uMIZqw5Fw3FHR25i2MQfk5j9bs2n28qRl-gur-RCM3lwUg |
上面就是一个符合规范的 JWT,其分为三个部分,用 . 隔开,依次是 Header,Payload 与 Signature。
Header 部分是一个 JSON 对象,存储了 JWT 的 Metadata,如下面所示。
1 | { |
其中的 alg 是用来签名的算法,其中默认算法是 HS256,也就是 HMAC SHA256,这里的 RS256 则是 RSA SHA256。有关签名算法会在后文探讨。
typ 则表示 Token 的类型,JWT 都统一写成 JWT。
值得注意的是,我们最终拿到的 JWT 中这部分实际上是通过了 Base64URL 转换的。
Payload 部分解码如下。
1 | { |
这里则是存放实际的数据,RFC 7519 实际上定义了七个官方字段,但我们也可以定义任何私有字段。
七个官方字段:
- iss (issuer):签发人
- exp (expiration time):过期时间
- sub (subject):主题
- aud (audience):受众
- nbf (Not Before):生效时间
- iat (Issued At):签发时间
- jti (JWT ID):编号
同样的,最终呈现在 JWT 中的信息经过了 Base64URL 转换。
Signature 的部分是对 Header 和 Payload 的签名。首先,指定一个(如果使用的是不对称加密算法,这里的两次应该是对)密钥,然后使用 Header 指定的加密算法将 Header.Payload 产生签名。
至此,我们就了解了一个 JWT 的组成。
注意,JWT 的 Payload 部分本身是不加密的,请勿在里面存放敏感信息。
Base64URL
前文频繁提到的 Base64URL 是 Base64 编码标准的一个变体,它的设计目的是使编码结果可以作为文件名或 URL 地址使用。在标准 Base64 字母表中包含了一些对于 URL 和文件名来说无效的字符,因此 Base64URL 进行了适当的修改以避免这些问题。
Base64URL 采用与标准 Base64 相同的算法,但在以下方面有所不同:
- 将
+替换为-; - 将
/替换为_; - 不需要填充字符
=; - 禁止使用行分隔符。
如何使用 JWT?
服务端签发 JWT 后,客户端与服务端通信只要带上 JWT 即可,一种普遍的做法是将 JWT 存放于请求头的 Authorization 里。
1 | Authorization: Bearer <token> |
服务端收到 Token 后,只需要拿密钥验证签名即可。
结束了?
遇到跨域处理,对称性加密就没那么好用了。这个时候,我们就需要用到非对称性加密。比如上文提到的 RS256 就是一种非对称性加密,即每次生成一对公私钥,使用私钥加密,公钥验证。
身份提供服务将公钥暴露出去,其他服务端接收到来自此服务的 Token 只需要找到公钥验证即可。
暴露公钥也有相关的规范,也就是 JWK,全称 JSON Web Key。
JWK 是用于表示加密密钥的基于 JSON 的格式,其格式也有 RFC 7517 规约。
其常见参数总结如下:
- kty:密钥类型,标识使用的签名算法,必须存在;
- use:公钥的预期用途,例如
sig用于签名,enc用于加密; - key_ops:密钥允许的操作列表,为一个数组,可能的取值有
sign,verify等; - alg:密钥对应的算法;
- kid:密钥 ID,用于在 JWK Set 中匹配具体密钥;
视密钥签名算法而定可能有其他参数。
例如在文章一开头给出的示例中,我们的 RS256 公钥如下。
1 | -----BEGIN PUBLIC KEY----- |
其对应的 JWK 则为下面所示。
1 | { |
这里的 kid 是通过提取公钥的 DER 字节流,计算 SHA-256 哈希,然后转成 URL 安全的 Base64 字符串作为唯一的 kid。笔者查询了一下,并没有发现通用的 kid 生成规则,暂且这么办吧。
当需要将多个 JWK 组合在一起时,它们会被组织成一个 JSON Web 密钥 Set (JWKS),仅包含上述 JWK 的 JWKS 如下面所示。
1 | { |
一般,我们使用 /.well-known/jwks.json 暴露 JWKS。
一些小技巧
阮一峰老师的博客写了几条 JWT 相关的特点,可以看看。
对于强制过期以前的 Token,我的选择是在服务端存一个 token_version,每次强制下线时只需把这个版本改掉,接收的 JWT 直接与这个比对就行。但这样不好跨站?需要研究下。
完结撒花 o( ̄▽ ̄)ブ
- 标题: 浅谈 JWT (JSON Web Tokens)
- 作者: Gavin
- 创建于 : 2026-04-01 00:40:00
- 更新于 : 2026-04-01 00:40:00
- 链接: https://gavin-blog.pages.dev/2026/浅谈-jwt-json-web-tokens/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。