在微服务架构中,Token 认证是保障系统安全性的重要手段,常见的方式包括 JWT(JSON Web Token) 及 基于 Redis 的 Token 认证。本文将介绍 双Token 机制 及其具体实现。
用户登录
请求 API 资源
Authorization: Bearer
。401 Unauthorized
)。Token 过期时的处理
Access Token 过期,但 Refresh Token 仍有效 :
Refresh Token 过期或无效:
用户登出
import io.jsonwebtoken.*;
import java.util.Date;
public class JwtUtil {
private static final String SECRET_KEY = "your_secret_key";
private static final long ACCESS_TOKEN_EXPIRATION = 2 * 60 * 60 * 1000; // 2小时
private static final long REFRESH_TOKEN_EXPIRATION = 7 * 24 * 60 * 60 * 1000; // 7天
// 生成 Access Token
public static String generateAccessToken(String userId) {
return Jwts.builder()
.setSubject(userId)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + ACCESS_TOKEN_EXPIRATION))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
// 生成 Refresh Token
public static String generateRefreshToken(String userId) {
return Jwts.builder()
.setSubject(userId)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + REFRESH_TOKEN_EXPIRATION))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
}
public static Claims parseToken(String token) {
return Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
}
@RestController
@RequestMapping("/auth")
public class AuthController {
@PostMapping("/login")
public Map<String, String> login(@RequestBody LoginRequest request) {
// 1. 校验用户名和密码(省略)
// 2. 生成 Token
String accessToken = JwtUtil.generateAccessToken(request.getUsername());
String refreshToken = JwtUtil.generateRefreshToken(request.getUsername());
// 3. 存储 Refresh Token(示例使用 Redis)
redisTemplate.opsForValue().set("refresh_token:" + request.getUsername(), refreshToken, 7, TimeUnit.DAYS);
// 4. 返回 Token
Map<String, String> tokens = new HashMap<>();
tokens.put("accessToken", accessToken);
tokens.put("refreshToken", refreshToken);
return tokens;
}
}
@PostMapping("/refresh")
public ResponseEntity<Map<String, String>> refresh(@RequestHeader("Authorization") String refreshToken) {
// 1. 校验 Refresh Token
Claims claims = JwtUtil.parseToken(refreshToken);
String userId = claims.getSubject();
// 2. 检查 Redis 是否存储该 Refresh Token
String storedToken = redisTemplate.opsForValue().get("refresh_token:" + userId);
if (storedToken == null || !storedToken.equals(refreshToken)) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
// 3. 生成新的 Access Token
String newAccessToken = JwtUtil.generateAccessToken(userId);
// 4. 返回新的 Token
Map<String, String> tokens = new HashMap<>();
tokens.put("accessToken", newAccessToken);
return ResponseEntity.ok(tokens);
}
@PostMapping("/logout")
public ResponseEntity<Void> logout(@RequestHeader("Authorization") String accessToken) {
Claims claims = JwtUtil.parseToken(accessToken);
String userId = claims.getSubject();
// 删除 Redis 中的 Refresh Token
redisTemplate.delete("refresh_token:" + userId);
return ResponseEntity.ok().build();
}
机制 | 作用 | 过期时间 | 存储位置 |
---|---|---|---|
Access Token | 用于 API 认证 | 短(10分钟-2小时) | 前端 LocalStorage/SessionStorage |
Refresh Token | 用于刷新 Access Token | 长(7天-30天) | Redis/数据库 |
博客主页: 总是学不会.