14.4、JWT令牌管理
分类: Spring Cloud Security
JWT 令牌管理
JWT 令牌是微服务安全的重要组件。本节将学习 JWT 令牌管理。
本节将学习:JWT 令牌生成、令牌验证、令牌刷新,以及令牌存储。
JWT 令牌生成
令牌结构
JWT 令牌结构:
- Header:算法和类型
- Payload:用户信息和权限
- Signature:签名验证
生成令牌
@Service public class TokenService { @Value("${jwt.secret}") private String secret; @Value("${jwt.expiration}") private Long expiration; public String generateToken(UserDetails userDetails) { Map<String, Object> claims = new HashMap<>(); claims.put("sub", userDetails.getUsername()); claims.put("roles", userDetails.getAuthorities()); return createToken(claims, userDetails.getUsername()); } private String createToken(Map<String, Object> claims, String subject) { return Jwts.builder() .setClaims(claims) .setSubject(subject) .setIssuedAt(new Date(System.currentTimeMillis())) .setExpiration(new Date(System.currentTimeMillis() + expiration)) .signWith(SignatureAlgorithm.HS512, secret) .compact(); } }
配置密钥
jwt: secret: mySecretKey expiration: 86400000
令牌验证
验证令牌
@Service public class TokenService { public Boolean validateToken(String token, UserDetails userDetails) { final String username = extractUsername(token); return (username.equals(userDetails.getUsername()) && !isTokenExpired(token)); } public String extractUsername(String token) { return extractClaim(token, Claims::getSubject); } public Date extractExpiration(String token) { return extractClaim(token, Claims::getExpiration); } private <T> T extractClaim(String token, Function<Claims, T> claimsResolver) { final Claims claims = extractAllClaims(token); return claimsResolver.apply(claims); } private Claims extractAllClaims(String token) { return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); } private Boolean isTokenExpired(String token) { return extractExpiration(token).before(new Date()); } }
过滤器验证
@Component public class JwtAuthenticationFilter extends OncePerRequestFilter { @Autowired private TokenService tokenService; @Autowired private UserDetailsService userDetailsService; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { final String authHeader = request.getHeader("Authorization"); if (authHeader != null && authHeader.startsWith("Bearer ")) { final String jwt = authHeader.substring(7); final String username = tokenService.extractUsername(jwt); if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = userDetailsService.loadUserByUsername(username); if (tokenService.validateToken(jwt, userDetails)) { UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); SecurityContextHolder.getContext().setAuthentication(authToken); } } } filterChain.doFilter(request, response); } }
令牌刷新
刷新令牌生成
public String generateRefreshToken(UserDetails userDetails) { Map<String, Object> claims = new HashMap<>(); claims.put("sub", userDetails.getUsername()); claims.put("type", "refresh"); return createToken(claims, userDetails.getUsername(), refreshExpiration); }
刷新令牌
@PostMapping("/refresh") public ResponseEntity<?> refreshToken(@RequestBody RefreshTokenRequest request) { String refreshToken = request.getRefreshToken(); if (tokenService.validateToken(refreshToken, userDetails)) { String newAccessToken = tokenService.generateToken(userDetails); return ResponseEntity.ok(new TokenResponse(newAccessToken, refreshToken)); } return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); }
令牌存储
内存存储
@Service public class TokenStorageService { private final Map<String, String> tokenStore = new ConcurrentHashMap<>(); public void storeToken(String username, String token) { tokenStore.put(username, token); } public String getToken(String username) { return tokenStore.get(username); } public void removeToken(String username) { tokenStore.remove(username); } }
Redis 存储
@Service public class TokenStorageService { @Autowired private RedisTemplate<String, String> redisTemplate; public void storeToken(String username, String token) { redisTemplate.opsForValue().set("token:" + username, token, Duration.ofHours(1)); } public String getToken(String username) { return redisTemplate.opsForValue().get("token:" + username); } }
官方资源
- JWT 官方文档:https://jwt.io/
- Spring Security JWT:https://spring.io/guides/tutorials/spring-boot-oauth2/
本节小结
在本节中,我们学习了:
第一个是 JWT 令牌生成。 如何生成 JWT 令牌。
第二个是令牌验证。 如何验证 JWT 令牌。
第三个是令牌刷新。 如何刷新访问令牌。
第四个是令牌存储。 如何存储和管理令牌。
这就是 JWT 令牌管理。正确管理 JWT 令牌可以保证系统的安全性。
在下一节,我们将学习 Gateway 安全集成。