JSON Web Token(JWT)是一种基于JSON的开放标准(RFC 7519),用于在网络应用间安全传递信息,特别适用于身份验证和授权场景。它通过数字签名确保数据的完整性和真实性,同时避免了传统会话认证的局限性(如服务器端状态存储问题)。
一、JWT的基本结构
JWT由三部分组成,通过点(.)连接:
-
Header(头部)
-
包含令牌类型("typ": "JWT")和签名算法(如HMAC SHA256、RSA等)
-
示例:
json{ "alg": "HS256", "typ": "JWT" }
-
这部分会被Base64Url编码
-
-
Payload(负载)
-
存放声明信息(claims),包括:
- 标准注册声明:如iss(发行人)、exp(过期时间)、sub(主题)、aud(受众)等
- 公共声明:可自定义但需避免命名冲突
- 私有声明:为双方协商使用的自定义数据
-
示例:
json{ "sub": "user123", "name": "John Doe", "admin": true, "exp": 1735689600 }
-
这部分也会被Base64Url编码,但不加密,因此应避免存储敏感信息
-
-
Signature(签名)
-
对Base64编码后的头部和负载进行签名,使用密钥(对称加密)或私钥(非对称加密)
-
签名公式示例(HS256算法):
scssHMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
-
用于确保数据未被篡改
-
最终的JWT格式为:Header.Payload.Signature
,例如:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
二、JWT的工作原理
- 用户认证:客户端提交用户名和密码至认证服务
- 生成JWT:服务器验证凭据后,生成包含用户信息的JWT并签名
- 存储与传输 :客户端将JWT存储在本地(如Cookie、LocalStorage),并在后续请求的HTTP头(如
Authorization: Bearer <token>
)中携带 - 验证与授权:服务器解析JWT,验证签名有效性、过期时间及权限,若合法则返回资源
三、JWT的核心优势
- 无状态认证:无需服务器存储会话信息,适合分布式系统和高并发场景
- 跨域支持:可在不同服务间传递,适用于单点登录(SSO)和微服务架构
- 安全性:签名机制防止数据篡改,支持非对称加密增强安全性
- 扩展性:JSON格式灵活,可自定义负载内容
- 轻量级:紧凑的字符串格式,传输开销小,各种语言都有库支持
四、JWT的局限性
- 不可撤销性:JWT有效期内无法强制失效,需依赖短有效期或黑名单机制
- 负载数据暴露:Base64编码可被解码,需避免存储敏感信息
- 签名密钥管理:密钥泄露会导致安全风险,需严格保护
- 体积问题:携带信息过多会导致token长度增加,影响传输效率
- 无法主动推送:服务端无法像Session那样主动通知客户端续约或失效
五、JWT的应用场景
- 单点登录(SSO):用户一次登录后访问多个关联系统
- API认证:微服务间通过JWT传递身份信息,减少频繁鉴权开销
- 移动端认证:无Cookie依赖,适合App与后端通信
- 信息交换:不同系统之间安全传递信息,如Istio服务网格中的身份传递
- 密码重置:生成包含重置信息的临时令牌
六、安全建议
- 使用HTTPS:防止JWT在传输中被截获
- 短有效期:设置合理过期时间(如30分钟),减少令牌泄露后的风险窗口
- 密钥轮换:定期更新签名密钥以增强安全性
- 避免敏感数据:负载中仅存储必要信息,敏感数据应加密
- 签名算法:选择强签名算法(如HS256或RS256),避免使用"None"算法
- 黑名单机制:对于注销功能,可使用Redis存储失效token
七、代码实现示例
Java实现(使用jjwt库)
typescript
// 生成JWT
public static String generateJwt(SecretKey secretKey) {
Map<String, Object> claims = new HashMap<>();
claims.put("userId", "12345");
claims.put("username", "john_doe");
claims.put("admin", true);
return Jwts.builder()
.setClaims(claims)
.setSubject("User Authentication")
.setIssuer("MyApp")
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 3600000)) // 1小时后过期
.signWith(secretKey)
.compact();
}
// 验证JWT
public static JwtResult validateJwt(String jwt, SecretKey secretKey) {
try {
Jws<Claims> claimsJws = Jwts.parserBuilder()
.setSigningKey(secretKey)
.build()
.parseClaimsJws(jwt);
return new JwtResult(true, claimsJws.getBody());
} catch (Exception e) {
return new JwtResult(false, null);
}
}
八、JWT与Session的比较
特性 | JWT | Session |
---|---|---|
状态管理 | 无状态 | 有状态(服务器存储) |
扩展性 | 适合分布式系统 | 需要会话共享机制 |
安全性 | 依赖签名和密钥 | 依赖Session ID安全性 |
存储位置 | 客户端存储 | 服务器存储 |
跨域支持 | 原生支持 | 需要额外配置 |
失效控制 | 依赖过期时间或黑名单 | 可立即失效 |
数据携带 | 可携带自定义数据 | 通常只存储会话标识 |
性能影响 | 每次请求需解析 | 需要查询会话存储 |
九、高级主题
- 刷新令牌机制:使用短期的访问令牌和长期的刷新令牌组合,平衡安全性与用户体验
- JWE(JSON Web Encryption):对敏感负载内容进行加密,增强隐私保护
- 多因素认证集成:在JWT claims中包含认证强度级别信息
- 微服务间的安全通信:使用JWT作为服务网格中的身份凭证
- 合规性考虑:满足GDPR等数据保护法规对令牌中个人信息存储的要求
十、总结
JWT通过紧凑的结构和安全的签名机制,成为现代无状态认证的主流方案。它特别适合分布式系统、微服务架构和跨域应用场景。然而,其设计需结合业务场景和安全需求,合理权衡便利性与风险。正确实现JWT认证需要考虑密钥管理、令牌有效期、传输安全等多方面因素,并可能需结合黑名单等补充机制来满足特定业务需求。