Java JWT 技术详解与实践指南
Java JWT 技术详解与实践指南
1. JWT 基础概念
JWT (JSON Web Token) 是一种开放标准(RFC 7519),用于在各方之间安全传输信息的紧凑且自包含的方式。常用于身份验证和授权场景。
1.1 JWT 结构
JWT 由三部分组成,用 .
分隔:
Header.Payload.Signature
- Header:包含令牌类型和签名算法(如 HS256、RS256)
- Payload:存放有效信息(如用户ID、角色、过期时间)
- Signature:验证令牌完整性和来源的签名
2. Java JWT 实现选型
2.1 常用库对比
库名称 | 特点 | Maven 依赖 |
---|---|---|
JJWT | 简单易用,文档完善 | io.jsonwebtoken:jjwt-api:0.12.3 |
Nimbus JOSE | 功能全面,支持更多JOSE规范 | com.nimbusds:nimbus-jose-jwt:9.37 |
3. 快速入门示例(使用 JJWT)
3.1 添加依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.3</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.12.3</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.12.3</version>
<scope>runtime</scope>
</dependency>
3.2 生成 JWT
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
public String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 3600000)) // 1小时
.signWith(SignatureAlgorithm.HS256, "your-256-bit-secret")
.compact();
}
3.3 解析验证 JWT
public String parseToken(String token) {
try {
return Jwts.parserBuilder()
.setSigningKey("your-256-bit-secret")
.build()
.parseClaimsJws(token)
.getBody()
.getSubject();
} catch (Exception e) {
throw new RuntimeException("Invalid JWT");
}
}
4. 最佳实践
4.1 安全配置
- 密钥管理:使用至少256位的安全密钥
- 过期时间:短期令牌(15-60分钟)结合刷新令牌
- 敏感信息:不要在Payload中存储密码等敏感数据
4.2 与 Spring Security 集成
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.addFilterBefore(new JwtFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
public class JwtFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
String token = extractToken(request);
if (token != null && validateToken(token)) {
Authentication auth = getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(auth);
}
filterChain.doFilter(request, response);
}
private String extractToken(HttpServletRequest request) {
String header = request.getHeader("Authorization");
if (header != null && header.startsWith("Bearer ")) {
return header.substring(7);
}
return null;
}
}
5. 高级功能
5.1 自定义 Claims
Map<String, Object> claims = new HashMap<>();
claims.put("roles", Arrays.asList("USER", "ADMIN"));
Jwts.builder()
.setClaims(claims)
// ...其他配置
5.2 刷新令牌机制
public String refreshToken(String oldToken) {
Claims claims = parseClaims(oldToken);
if (claims.getExpiration().before(new Date())) {
throw new RuntimeException("Token expired");
}
return generateToken(claims.getSubject());
}
6. 常见问题解决方案
6.1 签名验证失败
- 检查密钥是否一致
- 验证算法是否匹配
- 确认令牌未被篡改
6.2 跨服务验证(RS256 算法)
// 生成密钥对
KeyPair keyPair = Keys.keyPairFor(SignatureAlgorithm.RS256);
// 使用私钥签名
Jwts.builder().signWith(keyPair.getPrivate(), RS256)...
// 使用公钥验证
Jwts.parserBuilder().setSigningKey(keyPair.getPublic())...
7. 性能优化建议
- 缓存公钥:RS256算法中避免每次请求都读取公钥
- 异步验证:使用CompletableFuture进行并行验证
- 精简Claims:减少Payload数据量
8. 安全注意事项
- 始终使用HTTPS传输JWT
- 防范XSS攻击(避免localStorage存储,推荐HttpOnly Cookie)
- 定期轮换加密密钥
- 实现令牌黑名单机制