14.4JWT令牌管理

分类: 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 令牌生成。 如何生成 JWT 令牌。

第二个是令牌验证。 如何验证 JWT 令牌。

第三个是令牌刷新。 如何刷新访问令牌。

第四个是令牌存储。 如何存储和管理令牌。

这就是 JWT 令牌管理。正确管理 JWT 令牌可以保证系统的安全性。

在下一节,我们将学习 Gateway 安全集成。