Header
{
"alg": "HS256",
"typ": "JWT"
}
Payload
{
"sub": "123456",
"name": "John",
"iat": 1516239022
}
Signature
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
依赖配置:
<dependency>
<groupId>io.jsonwebtokengroupId>
<artifactId>jjwtartifactId>
<version>0.9.1version>
dependency>
配置文件:
# application.yml
jwt:
secret: your-256-bit-encryption-key
expiration: 7200 # 2小时
header: Authorization
工具类实现:
@Component
public class JwtUtils {
// 注入配置参数
@Value("${jwt.secret}")
private String secret;
// 生成Token
public String generateToken(UserDetails user) {
return Jwts.builder()
.setSubject(user.getUsername())
.claim("roles", user.getAuthorities())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
.signWith(SignatureAlgorithm.HS256, secret)
.compact();
}
// 解析Token
public Claims parseToken(String token) {
return Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
}
}
认证拦截器:
public class JwtInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) {
// 1. 检查白名单路径
if (isWhiteList(request.getRequestURI())) {
return true;
}
// 2. 获取并验证Token
String token = request.getHeader(jwtProperties.getHeader());
if (!jwtUtils.validateToken(token)) {
throw new UnauthorizedException("无效的访问凭证");
}
// 3. 注入用户信息
Claims claims = jwtUtils.parseToken(token);
request.setAttribute("userId", claims.getSubject());
return true;
}
private boolean isWhiteList(String uri) {
return Arrays.asList("/api/login", "/api/refresh").contains(uri);
}
}
注册拦截器:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtInterceptor())
.addPathPatterns("/api/**")
.excludePathPatterns("/api/auth/**");
}
}
流程图解:
刷新接口实现:
@PostMapping("/refresh")
public Result refreshToken(@RequestParam String refreshToken) {
if (redisTemplate.hasKey(refreshToken)) {
String username = redisTemplate.opsForValue().get(refreshToken);
UserDetails user = userService.loadUserByUsername(username);
String newAccessToken = jwtUtils.generateToken(user);
return Result.ok().data("accessToken", newAccessToken);
}
throw new BusinessException(600, "刷新令牌已失效");
}
五层防御体系:
黑名单实现:
@Component
public class TokenBlacklist {
@Autowired
private RedisTemplate<String, String> redisTemplate;
// Token加入黑名单(有效期剩余时间)
public void addToBlacklist(String token) {
Date expiration = jwtUtils.getExpirationFromToken(token);
long ttl = expiration.getTime() - System.currentTimeMillis();
redisTemplate.opsForValue().set(token, "invalid", ttl, TimeUnit.MILLISECONDS);
}
}
问题现象 | 根本原因 | 解决方案 |
---|---|---|
签名验证失败 | 密钥不一致或Token被篡改 | 统一密钥管理(配置中心) |
Token突然失效 | 服务器时间不同步 | 部署NTP时间同步服务 |
高并发下认证超时 | RSA算法性能瓶颈 | 切换为HS256算法 |
无法强制下线已登录用户 | 无状态特性导致 | 结合Redis维护短期Token黑名单 |
总结:JWT为现代分布式系统提供了优雅的认证解决方案,但实际落地时需综合考虑安全、性能、用户体验等多维度因素。建议结合具体业务场景选择合适的Token管理策略。