网关服务增加token刷新机制
All checks were successful
test/timeline-server/pipeline/head This commit looks good
All checks were successful
test/timeline-server/pipeline/head This commit looks good
This commit is contained in:
@@ -35,6 +35,7 @@ public class AuthenticationFilter implements GlobalFilter, Ordered {
|
|||||||
private RedisUtils redisUtils;
|
private RedisUtils redisUtils;
|
||||||
|
|
||||||
private static final String TOKEN_BLACKLIST_PREFIX = "auth:token:blacklist:";
|
private static final String TOKEN_BLACKLIST_PREFIX = "auth:token:blacklist:";
|
||||||
|
private static final String TOKEN_ACTIVE_PREFIX = "auth:token:active:";
|
||||||
|
|
||||||
// 需要跳过认证的路径列表
|
// 需要跳过认证的路径列表
|
||||||
private static final List<String> WHITELIST_PATHS = Arrays.asList(
|
private static final List<String> WHITELIST_PATHS = Arrays.asList(
|
||||||
@@ -55,33 +56,6 @@ public class AuthenticationFilter implements GlobalFilter, Ordered {
|
|||||||
return chain.filter(exchange);
|
return chain.filter(exchange);
|
||||||
}
|
}
|
||||||
|
|
||||||
// WebSocket 连接需要 token,但不强制要求(允许通过,由下游服务处理)
|
|
||||||
if (path.startsWith("/user/ws")) {
|
|
||||||
String token = extractToken(request);
|
|
||||||
if (token != null && !token.isEmpty()) {
|
|
||||||
// 如果有 token,验证并传递用户信息
|
|
||||||
if (jwtUtils.validateToken(token) && !isBlacklisted(token)) {
|
|
||||||
try {
|
|
||||||
Claims claims = jwtUtils.getClaimsFromToken(token);
|
|
||||||
if (claims != null && !jwtUtils.isTokenExpired(claims)) {
|
|
||||||
String userId = jwtUtils.getUserIdFromClaims(claims);
|
|
||||||
String username = jwtUtils.getUsernameFromClaims(claims);
|
|
||||||
ServerHttpRequest mutatedRequest = request.mutate()
|
|
||||||
.header("X-User-Id", userId)
|
|
||||||
.header("X-Username", username)
|
|
||||||
.build();
|
|
||||||
ServerWebExchange mutatedExchange = exchange.mutate().request(mutatedRequest).build();
|
|
||||||
return chain.filter(mutatedExchange);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
// token 验证失败,继续传递(由下游服务处理)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 即使没有 token 也允许通过,由下游服务决定是否接受连接
|
|
||||||
return chain.filter(exchange);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 从请求头或查询参数中获取token
|
// 从请求头或查询参数中获取token
|
||||||
String token = extractToken(request);
|
String token = extractToken(request);
|
||||||
|
|
||||||
@@ -104,6 +78,9 @@ public class AuthenticationFilter implements GlobalFilter, Ordered {
|
|||||||
return handleUnauthorized(exchange, "Token已过期");
|
return handleUnauthorized(exchange, "Token已过期");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 刷新token有效期
|
||||||
|
refreshTokenExpiration(token);
|
||||||
|
|
||||||
// 将用户信息添加到请求头中传递给下游服务
|
// 将用户信息添加到请求头中传递给下游服务
|
||||||
String userId = jwtUtils.getUserIdFromClaims(claims);
|
String userId = jwtUtils.getUserIdFromClaims(claims);
|
||||||
String username = jwtUtils.getUsernameFromClaims(claims);
|
String username = jwtUtils.getUsernameFromClaims(claims);
|
||||||
@@ -142,7 +119,7 @@ public class AuthenticationFilter implements GlobalFilter, Ordered {
|
|||||||
String[] params = query.split("&");
|
String[] params = query.split("&");
|
||||||
for (String param : params) {
|
for (String param : params) {
|
||||||
if (param.startsWith("Authorization=")) {
|
if (param.startsWith("Authorization=")) {
|
||||||
return param.substring(21);
|
return param.substring("Authorization=".length());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,6 +132,29 @@ public class AuthenticationFilter implements GlobalFilter, Ordered {
|
|||||||
return Boolean.TRUE.equals(exists);
|
return Boolean.TRUE.equals(exists);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加刷新token有效期的方法
|
||||||
|
private void refreshTokenExpiration(String token) {
|
||||||
|
try {
|
||||||
|
// 获取token的剩余有效时间
|
||||||
|
long remainingSeconds = jwtUtils.getRemainingSeconds(token);
|
||||||
|
|
||||||
|
// 获取总过期时间
|
||||||
|
long totalExpiration = jwtUtils.getAccessExpirationSeconds() != null ? jwtUtils.getAccessExpirationSeconds() : 900L;
|
||||||
|
|
||||||
|
// 定义一个阈值,当剩余时间少于总有效期的1/3时,刷新token在Redis中的活跃状态
|
||||||
|
long refreshThreshold = totalExpiration / 3;
|
||||||
|
|
||||||
|
if (remainingSeconds < refreshThreshold) {
|
||||||
|
// 在Redis中设置活跃token的记录,过期时间为完整的token有效期
|
||||||
|
// 这样可以在用户持续活动时保持会话活跃
|
||||||
|
redisUtils.expire(TOKEN_ACTIVE_PREFIX + token, totalExpiration);
|
||||||
|
log.debug("Token expiration refreshed for: {}, remaining seconds: {}", token, remainingSeconds);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("Failed to refresh token expiration: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("null")
|
@SuppressWarnings("null")
|
||||||
private Mono<Void> handleUnauthorized(ServerWebExchange exchange, String message) {
|
private Mono<Void> handleUnauthorized(ServerWebExchange exchange, String message) {
|
||||||
ServerHttpResponse response = exchange.getResponse();
|
ServerHttpResponse response = exchange.getResponse();
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ public class JwtUtils {
|
|||||||
@Value("${jwt.secret:timelineSecretKey}")
|
@Value("${jwt.secret:timelineSecretKey}")
|
||||||
private String secret;
|
private String secret;
|
||||||
|
|
||||||
|
@Value("${jwt.access-expiration:900}")
|
||||||
|
private Long accessExpirationSeconds;
|
||||||
|
|
||||||
private byte[] secretBytes() {
|
private byte[] secretBytes() {
|
||||||
return secret.getBytes(StandardCharsets.UTF_8);
|
return secret.getBytes(StandardCharsets.UTF_8);
|
||||||
}
|
}
|
||||||
@@ -64,4 +67,33 @@ public class JwtUtils {
|
|||||||
}
|
}
|
||||||
return claims.getSubject();
|
return claims.getSubject();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加获取过期时间的方法
|
||||||
|
public Date getExpirationDateFromToken(String token) {
|
||||||
|
Claims claims = getClaimsFromToken(token);
|
||||||
|
if (claims != null) {
|
||||||
|
return claims.getExpiration();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加获取剩余有效期的方法
|
||||||
|
public long getRemainingSeconds(String token) {
|
||||||
|
try {
|
||||||
|
Date exp = getExpirationDateFromToken(token);
|
||||||
|
if (exp == null) {
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
long diff = exp.getTime() - System.currentTimeMillis();
|
||||||
|
// 直接计算秒数而不是使用TimeUnit
|
||||||
|
return diff > 0 ? diff / 1000 : 0L;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加获取访问令牌过期秒数的方法
|
||||||
|
public Long getAccessExpirationSeconds() {
|
||||||
|
return accessExpirationSeconds;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user