一、JWT 是什么
JWT(JSON Web Token) 是一种基于 JSON 格式的令牌(Token),用于在双方之间以安全、可验证、可紧凑的方式传递信息。它通常用于 身份认证(Authentication) 和 信息交换(Information Exchange)。
它的关键特性:
- 自包含(self-contained):Token 内包含了所有必要的信息,不需要额外的数据库查询(除非做黑名单等额外验证)。
- 可签名(签名防篡改):使用 HMAC 或 RSA/ECDSA 等算法对数据签名,确保数据在传输过程中不被篡改。
- 可携带:体积小,可以通过 URL、HTTP Header、Cookie 等方式传输。
二、结构
JWT 是一个字符串,由 三部分 组成:
Header.Payload.Signature
2.1 Header(头部)
描述令牌类型和签名算法。
{
"alg": "HS256", // 签名算法(HMAC-SHA256)
"typ": "JWT" // 类型
}
编码方式:Base64Url 编码。
2.2 Payload(有效载荷)
存放声明(claims),即 Token 内的数据。分为三类:
- 注册声明(Registered Claims) — 官方建议字段:
iss
(Issuer):签发者sub
(Subject):面向的用户aud
(Audience):接收方exp
(Expiration Time):过期时间(时间戳,单位秒)nbf
(Not Before):在此时间之前不可用iat
(Issued At):签发时间jti
(JWT ID):唯一标识
- 公共声明(Public Claims) — 需要约定命名防冲突,比如
"role": "admin"
- 私有声明(Private Claims) — 应用内部自定义字段
示例:
{
"sub": "1234567890",
"name": "Alice",
"role": "admin",
"iat": 1699892400,
"exp": 1699896000
}
同样用 Base64Url 编码。
2.3 Signature(签名)
签名的本质是一个加密哈希值,由 JWT 的前两部分(Header 和 Payload)、密钥(或私钥)以及 Header 中指定的算法共同生成。其核心作用有两个:
- 防篡改:确保 JWT 在传输过程中(如从服务器到客户端,或客户端到服务器)未被恶意修改(例如篡改 Payload 中的用户权限、过期时间等)。
- 验真:证明该 JWT 确实是由合法的签发者(Issuer) 生成的,而非攻击者伪造。
签名的生成需要三个要素:
- 编码后的 Header(第一部分)
- 编码后的 Payload(第二部分)
- 密钥(对称加密算法)或私钥(非对称加密算法,由签发者持有)
- Header 中指定的哈希算法(如 HS256、RS256 等)
具体步骤
对 Header 和 Payload 进行 Base64Url 编码
前两部分(Header 和 Payload)会先被转换为 JSON 字符串,再通过Base64Url 编码(一种适合 URL 传输的 Base64 变体,替换了+
为-
、/
为_
,去除了末尾的=
)生成字符串,例如:
- 编码后的 Header:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
- 编码后的 Payload:
eyJzdWIiOiIxMjM0NTY3ODkiLCJuYW1lIjoiSm9obiBEb2UifQ
拼接编码后的字符串
将编码后的 Header 和 Payload 用点(.
)拼接,得到:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkiLCJuYW1lIjoiSm9obiBEb2UifQ
用指定算法和密钥生成签名
根据 Header 中alg
字段指定的算法(如HS256
),对拼接后的字符串进行哈希计算,并使用密钥(或私钥)进行加密,最终生成签名。
生成的签名是一串二进制数据,通常会转换为 Base64Url 编码的字符串,例如:SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
。
拼接完整 JWT
最终 JWT 为三部分用点拼接的字符串:Header编码.Payload编码.Signature编码。
计算方式
Signature = Sign( Base64UrlEncode(Header) + "." + Base64UrlEncode(Payload), secret_key )
如果使用 HMAC:
HMACSHA256(data, secret)
如果使用 RSA/ECDSA:
SignWithPrivateKey(data)
最终得到:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFsaWNlIiwicm9sZSI6ImFkbWluIn0.
hPxdv0L1Cp0VYH9Uaz9sP_kYPJ5cY5iEKnGgBwnD6WY
三、工作原理
以用户登录为例:
-
用户登录:提交用户名/密码给服务器。
-
服务器验证:验证成功后,生成 JWT,写入用户信息(例如 userId、角色等)。
-
返回 Token:JWT 发送给客户端(常通过
Authorization: Bearer <token>
)。 -
后续请求:客户端每次请求时带上 Token。
-
服务器验证:
检查签名是否有效。
检查
exp
、nbf
等时间字段。如果没问题,读取负载内容并处理请求。
特点:
- 无状态(Stateless):服务器不需要存储 Session 信息。
- 可扩展:多个服务间共享 JWT,无需共享 Session 存储。
四、JWT 与 Session 的对比
特性 | JWT | 传统 Session |
---|---|---|
存储位置 | 客户端 | 服务器(数据库/内存) |
是否无状态 | ✅ | ❌ |
扩展性 | 高,适合分布式 | 低 |
安全性 | 依赖签名 & 过期时间 | 依赖服务器存储 |
撤销 Token | 需要额外机制 | 直接删除 Session |
数据存储 | Token 内部存数据 | 存在服务器端 |
五、安全性
JWT 攻击是指用户向服务器发送修改过的 JWT 以实现恶意目的。通常,此目的是通过冒充其他已通过身份验证的用户来绕过身份验证和访问控制。
签名保证了防篡改,但不保证加密
- JWT 默认不加密,Payload 可以被任何人解码(Base64Url)。
- 如果需要加密内容,需要结合 JWE(JSON Web Encryption)。
常见安全问题
不验证签名 → 任意伪造 Token(致命)
算法混淆攻击(Alg None Attack):
- 如果服务端支持
"alg": "none"
且不做验证,攻击者可以删除签名绕过验证。
密钥过弱:
- HMAC 密钥长度太短会被暴力破解。
长时间有效期:
- Token 被盗后风险极大。
XSS/CSRF 攻击:
- 如果 Token 存在 localStorage,很容易被 JS 窃取。
六、优缺点
6.1 优点
跨语言支持好:JSON 通用格式,几乎所有语言有库支持。
无状态:适合分布式架构,不依赖中央 Session 存储。
可自包含:认证信息直接存储在 Token 中。
6.2 缺点
- 撤销困难:不像 Session 可以随时使无效,需要额外黑名单机制。
- 体积大:Base64 编码后长度比原始 JSON 大 33% 左右。
- 泄露风险高:Payload 可被解码,敏感信息不能直接放。
七、常用场景
前后端分离的身份认证
API 网关鉴权
微服务之间的信息传递
移动端 / 单页应用(SPA)登录状态保持
八、扩展协议
JWS(JSON Web Signature):JWT 的签名标准
JWE(JSON Web Encryption):JWT 的加密标准
JWK(JSON Web Key):密钥表示标准
JWA(JSON Web Algorithms):算法标准
九、JWT 的最佳实践
密钥管理:
- HMAC ≥ 256-bit 密钥
- 使用 RSA/ECDSA 时保护好私钥
短有效期 + 刷新机制:
- Access Token 有效期短(如 15 分钟)
- 使用 Refresh Token 延长会话
避免敏感信息:
- 不要在 Payload 存密码、银行卡号等
传输安全:
- 必须用 HTTPS
固定算法:
- 服务端强制使用指定算法,不允许
"alg": "none"
Token 存储:
- 推荐 HttpOnly Cookie(防 XSS)
- 避免 localStorage(容易被脚本读取)
登出机制:
- 数据库维护黑名单(撤销 Token)
- 刷新时检查黑名单
十、全流程图
1、认证与令牌签发
2、携带 JWT 调用与校验
3、刷新令牌与轮换机制
4、撤销与黑名单机制
5、完整生命周期
6、威胁与缓解措施