什么是 JWT?JSON Web Token 完整指南
什么是 JWT?
JSON Web Token(JWT)是一种开放标准(RFC 7519),它定义了一种紧凑、自包含的方式,以 JSON 对象的形式在各方之间安全地传输信息。JWT 是现代 Web 应用中最主流的令牌格式,用于用户身份验证、API 授权,以及微服务之间的安全数据交换等各种场景。
JWT 身份验证的工作原理
JWT 身份验证遵循一种无状态模式,无需服务器端会话存储。理解这一流程是理解 JWT 为何成为现代 API 安全默认选择的关键。
身份验证流程
- 用户登录——客户端向身份验证服务器发送凭据(用户名和密码)。
- 服务器创建 JWT——服务器验证凭据,创建一个包含用户身份和权限的 JWT,使用密钥或私钥进行签名,然后将其返回给客户端。
- 客户端存储令牌——客户端存储 JWT,通常存储在 localStorage、sessionStorage 或 HTTP-only cookie 中。
- 客户端在每次请求中发送令牌——对于每个后续 API 请求,客户端在
Authorization头中使用 Bearer 方案携带 JWT:Authorization: Bearer <token>。 - 服务器验证令牌——服务器验证 JWT 签名、检查过期时间,并提取用户声明以授权请求。
- 无需会话查询——由于 JWT 包含所有必要信息,服务器无需查询数据库或缓存即可识别用户。
为什么这很重要
传统的基于会话的身份验证将会话 ID 存储在服务器的内存或数据库中。每个经过身份验证的请求都需要一次查询。JWT 消除了这种查询——令牌本身就证明了用户的身份。这使得基于 JWT 的系统天然具有可扩展性,因为集群中的任何服务器都可以在无需共享会话状态的情况下验证令牌。
JWT 与基于会话的身份验证对比
| 方面 | JWT 身份验证 | 基于会话的身份验证 | |--------|--------------------|------------------------------| | 存储位置 | 客户端(在令牌中) | 服务器端(内存或数据库) | | 可扩展性 | 无需共享会话存储 | 跨实例需要共享会话存储 | | 跨域 | 天然支持跨域 | 会话共享困难 | | 移动端友好 | 是(无需 cookie) | 需要 cookie 支持 | | 撤销 | 困难(直到令牌过期) | 简单(从存储中删除会话) | | 载荷大小 | 每次请求较大(令牌在 header 中) | 最小(仅会话 ID) |
为什么 JWT 用于 API 安全
使 JWT 适合身份验证的特性,也使其成为保护 API 的理想选择。
无状态且可扩展
每个 JWT 都携带服务器授权请求所需的全部信息。无需查询会话数据库、无需检查缓存、无需跨服务器同步状态。负载均衡器可以将请求路由到任何服务器实例而无需协调。
跨域与跨源
JWT 天然支持在不同域名和源之间工作。由 auth.example.com 签发的令牌可以用于访问 api.example.com 和 admin.example.com。这使得 JWT 成为单点登录(SSO)系统的基础。
细粒度授权
JWT payload 可以包含用于角色、权限和组织成员资格的自定义声明:
{
"sub": "user_abc123",
"role": "admin",
"permissions": ["read:users", "write:users", "delete:users"],
"org_id": "org_xyz789",
"iat": 1516239022,
"exp": 1516325422
}
API 服务器直接从令牌中读取这些声明,无需任何额外查询。
JWT 用于单点登录(SSO)
单点登录是 JWT 最常见的用例之一。在典型的 SSO 架构中:
- 用户登录身份提供方(IdP)——例如 Auth0、Okta 或 Keycloak。
- IdP 使用其私钥签发一个 JWT。
- 用户将该 JWT 提供给多个应用程序。
- 每个应用程序使用 IdP 的公钥验证签名。
- 每个应用程序信任该令牌,而无需在每次请求时与 IdP 通信。
这种模式使得一次登录即可授权访问数十种服务——电子邮件、文件存储、项目管理、人力资源系统——而无需每个服务各自实现自己的登录流程。
JWT 安全最佳实践
虽然 JWT 为 API 安全提供了坚实的基础,但正确的实现至关重要。
保持令牌短生命周期
将 exp(过期时间)声明设置为较短的时长——访问令牌设为 15 分钟到 1 小时。使用生命周期更长的刷新令牌(数天或数周)来获取新的访问令牌,而无需用户重新进行身份验证。
始终使用 HTTPS
JWT 不会对其 payload 进行加密。任何截获令牌的人都可以解码并读取其内容。始终通过 HTTPS 传输 JWT 以防止被截获。
验证一切
始终验证签名,检查令牌是否已过期,确认 aud(受众)与您的应用程序匹配,并拒绝使用意外算法的令牌。
安全存储令牌
在客户端,尽可能将 JWT 存储在 HTTP-only cookie 中,或存储在单页应用的内存中。避免将 localStorage 用于可访问敏感数据的令牌。
常见的 JWT 误解
"JWT 是加密的。"——不是。JWT 是签名的,而非加密的。payload 经过 base64url 编码,这是可逆的。JWE(JSON Web Encryption)提供加密,但它是单独的规范,不包含在标准 JWT 中。
"JWT 可以防止 CSRF。"——仅凭自身并不行。JWT 放在 Authorization 头中有所帮助,但如果令牌在 cookie 中,仍然需要 CSRF 保护。
"JWT 仅用于身份验证。"——JWT 用于身份验证,但也用于授权数据交换、无状态会话以及服务之间的安全信息共享。
在线解码和探索 JWT
使用 JWT 解码器工具来检查任何 JSON Web Token。粘贴您的令牌,即可立即看到解码后的 header、payload 和签名算法。该工具帮助您了解令牌携带哪些数据,并验证它们的结构是否正确。