浅谈 JWT (JSON Web Tokens)

Gavin

本文假定您有基础的 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
2
3
4
{
"alg": "RS256",
"typ": "JWT"
}

其中的 alg 是用来签名的算法,其中默认算法是 HS256,也就是 HMAC SHA256,这里的 RS256 则是 RSA SHA256。有关签名算法会在后文探讨。

typ 则表示 Token 的类型,JWT 都统一写成 JWT

值得注意的是,我们最终拿到的 JWT 中这部分实际上是通过了 Base64URL 转换的。

Payload 部分解码如下。

1
2
3
4
5
{
"sub": "Gavin",
"iat": 1774970823,
"exp": 1774974423
}

这里则是存放实际的数据,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:密钥允许的操作列表,为一个数组,可能的取值有 signverify 等;
  • alg:密钥对应的算法;
  • kid:密钥 ID,用于在 JWK Set 中匹配具体密钥;

视密钥签名算法而定可能有其他参数。

例如在文章一开头给出的示例中,我们的 RS256 公钥如下。

1
2
3
4
5
6
7
8
9
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArQOjkox78MYLBZEAhuOP
L2bTDulSpjl6G6qARp2/njwh+zvtHKGU63QwAhCtJS37k9rZio5tDbBmtlJwlrgw
f+UWgc7GtV9EC1Jgdwd6yGpsI8ZWs7UWH/eJDwi6NP2qbwBha7UtQyzf1cOfJk5y
lT6FyCsHGy1ssYiEK42keXd7RtuoS6C/QhfgWyf28Mwjj63TBmuOY6fZ8UPhvQCe
dmHkfW9tgWHOsz3qI5vKrY9MifDnh1wU6k6/A9MtXAKHd95aBEjHzF+y1rAly+rE
ZnefLvt/gvIqaoa0Yy2VpKqTSu17u+AoyDskUDolIN55w/2Z7OQR/8poHKTOSpwF
9wIDAQAB
-----END PUBLIC KEY-----

其对应的 JWK 则为下面所示。

1
2
3
4
5
6
7
8
9
{
"kty": "RSA",
"key_ops": ["verify"],
"n": "vumBc5W0z-bDNVf-z_qR2qoey9EXp3YhsWCGBjbxxjfJvDUT9ptqENCxAOh4uZlAjWOkkUa0Ako104cY5A4myvrJbuxbLai7oknq6d5pBZxYJHlA5XroQ2vfDe3mcHUePcMIsSMo-hZQ5bcTvKxGW2idKuPWUxAUtnkxZgjF9uzh3IqpFHyZ5swN4zCZ89JLGUb7Vzb5bthziqm71cXpDQPx-RvJZ47DAQfioMplogRAh_VdnVPRknHxABbn9eTR_OhcxdhGTPSb4Bz-1iagZydyID4CPYOYRY9Y_7-q4c_zx1b-M8HHWmFpI3quN21N42LvAJK6nL7hGCQrm9m9lQ",
"e": "AQAB",
"kid": "key-H-_edi7gYRGFhkfiFHpUpw",
"alg": "RS256",
"use": "sig"
}

这里的 kid 是通过提取公钥的 DER 字节流,计算 SHA-256 哈希,然后转成 URL 安全的 Base64 字符串作为唯一的 kid。笔者查询了一下,并没有发现通用的 kid 生成规则,暂且这么办吧。

当需要将多个 JWK 组合在一起时,它们会被组织成一个 JSON Web 密钥 Set (JWKS),仅包含上述 JWK 的 JWKS 如下面所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"keys": [
{
"kty": "RSA",
"key_ops": [
"verify"
],
"n": "vumBc5W0z-bDNVf-z_qR2qoey9EXp3YhsWCGBjbxxjfJvDUT9ptqENCxAOh4uZlAjWOkkUa0Ako104cY5A4myvrJbuxbLai7oknq6d5pBZxYJHlA5XroQ2vfDe3mcHUePcMIsSMo-hZQ5bcTvKxGW2idKuPWUxAUtnkxZgjF9uzh3IqpFHyZ5swN4zCZ89JLGUb7Vzb5bthziqm71cXpDQPx-RvJZ47DAQfioMplogRAh_VdnVPRknHxABbn9eTR_OhcxdhGTPSb4Bz-1iagZydyID4CPYOYRY9Y_7-q4c_zx1b-M8HHWmFpI3quN21N42LvAJK6nL7hGCQrm9m9lQ",
"e": "AQAB",
"kid": "key-H-_edi7gYRGFhkfiFHpUpw",
"alg": "RS256",
"use": "sig"
}
]
}

一般,我们使用 /.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 进行许可。