JWT Token 认证技术详解
·更新于 2026-06-08
概述
本文档详细介绍校园招聘管理平台中 JWT(JSON Web Token)认证机制的实现原理、核心组件和使用方式。
JWT 认证流程
┌────────┐ ┌────────┐ ┌────────────┐ │ Client │ ──── │ Server │ ──── │ Database │ └────────┘ └────────┘ └────────────┘ │ │ │ │ 1.登录请求 │ │ │──────────────>│ │ │ │ 2.验证用户 │ │ │────────────────>│ │ │ 3.返回用户信息 │ │ │<────────────────│ │ │ │ │ 4.生成Token │ │ │<──────────────│ │ │ token+refresh │ │ │ │ │ │ 5.请求资源 │ │ │──────────────>│ │ │ Authorization │ │ │ : Bearer xxx │ │
|
核心技术组件
1. JwtTokenProvider
JwtTokenProvider 是 JWT 令牌的核心提供者,负责令牌的生成、验证和解析。
@Component public class JwtTokenProvider {
@Value("${jwt.secret:ThisIsAVerySecureSecretKeyForJWTTokenGeneration2024}") private String jwtSecret;
@Value("${jwt.expiration:3600000}") private long jwtExpiration;
@Value("${jwt.refresh-expiration:604800000}") private long refreshExpiration;
public String generateToken(String username, String role, Long userId) { Date now = new Date(); Date expiryDate = new Date(now.getTime() + jwtExpiration);
return Jwts.builder() .subject(username) .claim("userId", userId) .claim("role", role) .issuedAt(now) .expiration(expiryDate) .signWith(getSigningKey()) .compact(); } }
|
2. JwtAuthenticationFilter
JwtAuthenticationFilter 是 Spring Security 的过滤器,负责每个请求的 JWT 验证。
@Component public class JwtAuthenticationFilter extends OncePerRequestFilter {
private static final String AUTHORIZATION_HEADER = "Authorization"; private static final String BEARER_PREFIX = "Bearer ";
@Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String jwt = getJwtFromRequest(request);
if (StringUtils.hasText(jwt)) { if (!jwtTokenProvider.validateToken(jwt)) { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); return; }
String username = jwtTokenProvider.getUsernameFromToken(jwt); String role = jwtTokenProvider.getRoleFromToken(jwt);
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(role); UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(username, null, Collections.singletonList(authority));
SecurityContextHolder.getContext().setAuthentication(authentication); }
filterChain.doFilter(request, response); } }
|
令牌结构
Access Token
存储的信息:
sub: 用户名
userId: 用户ID
role: 角色(包含 ROLE_ 前缀)
iat: 签发时间
exp: 过期时间
Refresh Token
与 Access Token 的区别:
- 有效期更长(7天 vs 1小时)
- 包含
type: "refresh" 声明
- 存储在数据库中,支持令牌失效和续期
安全特性
1. 签名验证
使用 HMAC-SHA256 算法签名,确保令牌不可伪造:
private SecretKey getSigningKey() { return Keys.hmacShaKeyFor(jwtSecret.getBytes(StandardCharsets.UTF_8)); }
|
2. 多种异常处理
public boolean validateToken(String token) { try { Jwts.parser() .verifyWith(getSigningKey()) .build() .parseSignedClaims(token); return true; } catch (SecurityException ex) { log.error("无效的JWT签名: {}", ex.getMessage()); } catch (MalformedJwtException ex) { log.error("无效的JWT令牌: {}", ex.getMessage()); } catch (ExpiredJwtException ex) { log.error("JWT令牌已过期: {}", ex.getMessage()); } catch (UnsupportedJwtException ex) { log.error("不支持的JWT令牌: {}", ex.getMessage()); } catch (IllegalArgumentException ex) { log.error("JWT令牌为空: {}", ex.getMessage()); } return false; }
|
3. 跳过验证的路径
@Override protected boolean shouldNotFilter(HttpServletRequest request) { String path = request.getRequestURI(); if (path.contains("/auth/refresh")) { return true; } return false; }
|
使用示例
登录流程
public LoginResponse login(LoginRequest request) { User user = findByUsername(request.getUsername()) .orElseThrow(() -> new BusinessException(1011, "用户名或密码错误"));
if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) { throw new BusinessException(1011, "用户名或密码错误"); }
String token = jwtTokenProvider.generateToken( user.getUsername(), user.getRole().name(), user.getId() );
String refreshToken = jwtTokenProvider.generateRefreshToken( user.getUsername(), user.getRole().name(), user.getId() );
user.setRefreshToken(refreshToken); user.setRefreshTokenExpireTime(LocalDateTime.now().plusDays(7)); saveUser(user);
return new LoginResponse(token, user.getId(), user.getUsername(), user.getRole().name(), refreshToken, refreshTokenExpireTimeMillis); }
|
Token 刷新流程
public LoginResponse refreshToken(RefreshTokenRequest request) { String refreshToken = request.getRefreshToken();
if (!jwtTokenProvider.validateRefreshToken(refreshToken)) { throw new BusinessException(1020, "刷新令牌无效或已过期"); }
User user = findByRefreshToken(refreshToken) .orElseThrow(() -> new BusinessException(1020, "刷新令牌无效或已过期"));
if (!refreshToken.equals(user.getRefreshToken())) { throw new BusinessException(1020, "刷新令牌无效或已过期"); }
String newToken = jwtTokenProvider.generateToken( user.getUsername(), user.getRole().name(), user.getId() );
return new LoginResponse(newToken, user.getId(), user.getUsername(), user.getRole().name(), user.getRefreshToken(), user.getRefreshTokenExpireTime().atZone(ZoneId.systemDefault()) .toInstant().toEpochMilli()); }
|
前端请求示例
const response = await fetch('/api/jobs', { headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json' } });
|
配置参数
| 参数 |
默认值 |
说明 |
jwt.secret |
ThisIsAVerySecureSecretKeyForJWTTokenGeneration2024 |
签名密钥 |
jwt.expiration |
3600000 |
Access Token 有效期(毫秒) |
jwt.refresh-expiration |
604800000 |
Refresh Token 有效期(毫秒) |
依赖版本
io.jsonwebtoken:jjwt-api:0.12.6
jjwt-impl:0.12.6
jjwt-jackson:0.12.6
参考资料