首页 前端知识 JSON Web Token (JWT) 学习笔记总结

JSON Web Token (JWT) 学习笔记总结

2025-03-14 12:03:42 前端知识 前端哥 20 223 我要收藏

深入理解JSON Web Token (JWT)

引言

在现代Web应用架构中,身份认证和授权机制是确保系统安全的核心环节。随着分布式系统和微服务架构的普及,传统的基于会话(Session)的认证方式面临着诸多挑战。JSON Web Token (JWT) 作为一种轻量级、自包含的安全令牌,已经成为解决这些挑战的重要技术选择。

本文将深入探讨JWT的核心概念、工作原理以及在实际项目中的应用。无论你是刚接触JWT的初学者,还是希望进一步提升相关技能的资深开发者,这篇文章都将帮助你更全面地理解和有效地使用JWT技术。

背景知识

什么是JWT?

JWT是一种开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间安全地传输信息作为JSON对象。这些信息可以被验证和信任,因为它是经过数字签名的。

JWT的基本结构

JWT由三部分组成,以点(.)分隔:

  1. Header(头部):包含令牌类型和使用的签名算法
  2. Payload(载荷):包含声明(claims),即实际传递的数据
  3. Signature(签名):用于验证消息在传输过程中没有被更改

每个部分都是Base64Url编码的,最终形成的结构如下:

xxxxx.yyyyy.zzzzz
复制

其中xxxxx是Header,yyyyy是Payload,zzzzz是Signature。

JWT的工作原理

JWT的工作流程通常如下:

  1. 用户成功登录后,服务器创建一个JWT
  2. 服务器将JWT返回给客户端
  3. 客户端在后续请求中包含JWT(通常在Authorization头部)
  4. 服务器验证JWT并处理请求

这种方式使服务器可以无状态地验证用户身份,无需在服务器端存储会话信息,特别适合分布式系统和微服务架构。

JWT的核心内容

创建JWT

创建JWT需要三个步骤:定义头部、构建载荷和生成签名。

1. 头部(Header)

头部通常由两部分组成:令牌类型(typ)和所使用的签名算法(alg):

{
"alg": "HS256",
"typ": "JWT"
}
复制

这里的HS256表示使用HMAC SHA-256算法进行签名。

2. 载荷(Payload)

载荷包含声明(claims),即我们要传递的数据。声明分为三种类型:

  • 注册声明(Registered Claims):预定义的声明,如iss(发行者)、exp(过期时间)、sub(主题)等
  • 公共声明(Public Claims):可以由使用JWT的各方自定义,但为避免冲突,应该在IANA JSON Web Token Registry中定义
  • 私有声明(Private Claims):用于在使用JWT的各方之间共享信息的自定义声明

例如:

{
"sub": "1234567890",
"name": "John Doe",
"admin": true,
"iat": 1516239022,
"exp": 1516242622
}
复制
3. 签名(Signature)

签名部分是对前两部分的签名,用于验证消息的完整性。签名的计算方式如下:

HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
复制

其中secret是一个私钥,只有服务器知道。

使用Java生成JWT的示例代码

以下是使用流行的jjwt库创建JWT的Java代码示例:

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import java.security.Key;
import java.util.Date;
public class JwtGenerator {
public static String generateJwt(String subject, String username, boolean isAdmin) {
// 创建安全的密钥
Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
// 当前时间
long now = System.currentTimeMillis();
// 过期时间设置为1小时
long expirationTime = now + 3600000;
// 构建JWT
return Jwts.builder()
.setSubject(subject)
.claim("name", username)
.claim("admin", isAdmin)
.setIssuedAt(new Date(now))
.setExpiration(new Date(expirationTime))
.signWith(key)
.compact();
}
public static void main(String[] args) {
String jwt = generateJwt("1234567890", "John Doe", true);
System.out.println(jwt);
}
}
复制

验证JWT

验证JWT是确保令牌有效性的关键步骤,主要包括以下几个方面:

  1. 验证签名是否有效
  2. 检查令牌是否过期
  3. 验证发行者(issuer)是否正确
  4. 验证接收者(audience)是否正确
使用Java验证JWT的示例代码
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import java.security.Key;
public class JwtValidator {
public static boolean validateJwt(String jwtString, Key key) {
try {
// 解析JWT
Jws<Claims> jwt = Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(jwtString);
// JWT解析成功,签名验证通过
System.out.println("JWT验证成功");
System.out.println("Subject: " + jwt.getBody().getSubject());
System.out.println("Name: " + jwt.getBody().get("name"));
System.out.println("Admin: " + jwt.getBody().get("admin"));
System.out.println("Expiration: " + jwt.getBody().getExpiration());
return true;
} catch (JwtException e) {
// JWT验证失败
System.out.println("JWT验证失败: " + e.getMessage());
return false;
}
}
}
复制

JWT的优点与缺点

优点
  1. 无状态:服务器不需要存储会话信息,降低了服务器的负载
  2. 跨域支持:可以在不同域的系统之间轻松传递
  3. 扩展性:适用于分布式系统和微服务架构
  4. 自包含:包含了所有必要的用户信息,减少了数据库查询
  5. 安全性:签名确保了信息不被篡改
  6. 支持移动平台:适用于本地存储,便于移动应用使用
缺点
  1. 令牌大小:JWT可能包含大量信息,导致令牌体积增大
  2. 无法撤销:一旦签发,在过期前无法轻易撤销
  3. 安全存储:客户端存储令牌的安全性问题
  4. 密钥管理:密钥泄露会导致系统安全性受损
  5. 过期处理:需要合理设计过期策略和刷新机制

实践应用

案例研究:电商平台的身份认证系统

考虑一个电商平台的微服务架构系统,包含用户服务、商品服务、订单服务和支付服务。这种场景下,JWT可以作为用户身份认证的核心机制。

实现流程
  1. 用户登录

    • 用户提供凭证(用户名/密码)
    • 认证服务验证凭证并生成JWT
    • JWT包含用户ID、角色和权限信息
    • 客户端存储JWT
  2. 服务间调用

    • 客户端在请求头中包含JWT
    • 各个微服务验证JWT的有效性
    • 基于JWT中的信息执行授权逻辑
代码示例:Spring Boot中的JWT实现
@RestController
public class AuthController {
@Autowired
private UserService userService;
private final String SECRET_KEY = "your-secret-key";
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) {
// 验证用户凭证
User user = userService.authenticate(
loginRequest.getUsername(),
loginRequest.getPassword()
);
if (user == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body("Invalid credentials");
}
// 创建JWT
String token = Jwts.builder()
.setSubject(user.getId().toString())
.claim("username", user.getUsername())
.claim("roles", user.getRoles())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 86400000)) // 24小时
.signWith(Keys.hmacShaKeyFor(SECRET_KEY.getBytes()))
.compact();
// 返回JWT
return ResponseEntity.ok(new JwtResponse(token));
}
}
复制

最佳实践

  1. 合理设置过期时间

    • 短期令牌(如1小时)用于正常访问
    • 长期刷新令牌用于获取新的访问令牌
  2. 实现令牌刷新机制

    @PostMapping("/refresh")
    public ResponseEntity<?> refreshToken(@RequestBody RefreshTokenRequest request) {
    try {
    // 验证刷新令牌
    Claims claims = Jwts.parserBuilder()
    .setSigningKey(Keys.hmacShaKeyFor(SECRET_KEY.getBytes()))
    .build()
    .parseClaimsJws(request.getRefreshToken())
    .getBody();
    // 创建新的访问令牌
    String newToken = Jwts.builder()
    .setSubject(claims.getSubject())
    .claim("username", claims.get("username"))
    .claim("roles", claims.get("roles"))
    .setIssuedAt(new Date())
    .setExpiration(new Date(System.currentTimeMillis() + 3600000)) // 1小时
    .signWith(Keys.hmacShaKeyFor(SECRET_KEY.getBytes()))
    .compact();
    return ResponseEntity.ok(new JwtResponse(newToken));
    } catch (Exception e) {
    return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
    .body("Invalid refresh token");
    }
    }
    复制
  3. 使用HTTPS:始终通过HTTPS传输JWT,防止令牌被窃取

  4. 最小权限原则:JWT中仅包含必要的信息,避免敏感数据

  5. 密钥轮换:定期更换签名密钥,提高系统安全性

  6. 黑名单机制:对于高安全要求的系统,实现令牌黑名单

常见问题解答

如何安全地存储JWT?

客户端存储

  • 浏览器:HttpOnly Cookie(防止XSS攻击)或localStorage(更易于访问但安全性较低)
  • 移动应用:安全存储机制(如KeyStore或Keychain)

最佳实践

// 前端存储JWT(使用HttpOnly Cookie)
// 服务端设置
response.cookie('jwt', token, {
httpOnly: true,
secure: true, // 仅HTTPS
sameSite: 'strict' // 防止CSRF
});
// 前端发送请求
fetch('/api/data', {
credentials: 'include' // 包含cookie
});
复制

如何处理JWT令牌泄露?

  1. 设置较短的过期时间:限制泄露令牌的有效窗口
  2. 实现令牌黑名单:允许显式撤销令牌
  3. 包含设备指纹:将令牌与特定设备绑定
  4. 监控异常活动:检测可能的令牌滥用

JWT vs. Session Authentication?

特性JWTSession
存储位置客户端服务器端
可扩展性
状态无状态有状态
撤销能力较难容易
安全性依赖签名依赖会话ID
性能减少数据库查询需要会话查找

总结

JWT作为一种现代化的身份认证和信息传递机制,具有无状态、自包含和易于扩展等优势,特别适合分布式系统和微服务架构。通过本文的讲解,我们详细了解了JWT的结构、工作原理、创建和验证过程,以及在实际应用中的最佳实践。

理解和掌握JWT技术对于现代Web开发人员至关重要。随着分布式系统的普及,JWT已经成为解决跨服务身份认证问题的重要工具。

然而,JWT并非万能的解决方案,开发者需要根据具体的应用场景和安全需求,合理评估使用JWT的利弊,并采取适当的措施来确保系统的安全性。

参考资料

  1. JWT官方文档: https://jwt.io/introduction/
  2. RFC 7519 - JSON Web Token: https://tools.ietf.org/html/rfc7519
  3. JJWT库文档: https://github.com/jwtk/jjwt
  4. Spring Security JWT集成指南: https://spring.io/guides/tutorials/spring-security-and-angular-js/
  5. OWASP JWT Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_Cheat_Sheet.html
转载请注明出处或者链接地址:https://www.qianduange.cn//article/23656.html
标签
评论
会员中心 联系我 留言建议 回顶部
复制成功!