This commit is contained in:
孙小云 2026-02-02 15:43:41 +08:00
parent d80502c681
commit 2b702197fa
1 changed files with 96 additions and 28 deletions

View File

@ -5,11 +5,18 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
/** /**
* WVP Access Token 过滤器 * WVP Access Token 过滤器
* 用于为 WVP 请求添加或替换 access-token 请求头 * 用于为 WVP 请求添加或替换 access-token 请求头
@ -19,9 +26,22 @@ public class WvpAccessTokenFilter extends AbstractGatewayFilterFactory<WvpAccess
private static final Logger log = LoggerFactory.getLogger(WvpAccessTokenFilter.class); private static final Logger log = LoggerFactory.getLogger(WvpAccessTokenFilter.class);
/**
* WVP token 过期时间分钟- WVP 源码中获取
*/
private static final long TOKEN_EXPIRATION_MINUTES = 30;
/**
* 提前刷新时间分钟- token 过期前 5 分钟刷新
*/
private static final long REFRESH_BEFORE_EXPIRATION_MINUTES = 5;
private static String cachedAccessToken = null; private static String cachedAccessToken = null;
private static LocalDateTime tokenExpirationTime = null;
private static final Object LOCK = new Object(); private static final Object LOCK = new Object();
private final WebClient webClient = WebClient.builder().build();
public WvpAccessTokenFilter() { public WvpAccessTokenFilter() {
super(Config.class); super(Config.class);
} }
@ -29,53 +49,100 @@ public class WvpAccessTokenFilter extends AbstractGatewayFilterFactory<WvpAccess
@Override @Override
public GatewayFilter apply(Config config) { public GatewayFilter apply(Config config) {
return (exchange, chain) -> { return (exchange, chain) -> {
try { String path = exchange.getRequest().getURI().getPath();
// 获取 access token String query = exchange.getRequest().getURI().getQuery();
String accessToken = getAccessToken(); String fullPath = query != null ? path + "?" + query : path;
log.info("WVP 请求 - 原始路径: {}", exchange.getRequest().getURI().getPath()); log.info("WVP 请求 - 路径: {}", fullPath);
log.info("WVP 请求 - 添加 access-token");
// 修改请求添加或替换 access-token 请求头 // 使用 WebClient 直接调用 WVP支持自动重试
ServerHttpRequest modifiedRequest = exchange.getRequest().mutate() return callWvpWithRetry(fullPath, exchange, 0)
.header("access-token", accessToken) .flatMap(responseBody -> {
.build(); // 将响应写回客户端
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.OK);
response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
// 创建新的 exchange DataBufferFactory bufferFactory = response.bufferFactory();
ServerWebExchange modifiedExchange = exchange.mutate() DataBuffer buffer = bufferFactory.wrap(responseBody.getBytes(StandardCharsets.UTF_8));
.request(modifiedRequest)
.build();
return chain.filter(modifiedExchange).then(Mono.fromRunnable(() -> { return response.writeWith(Mono.just(buffer));
log.info("WVP 响应 - Status: {}", modifiedExchange.getResponse().getStatusCode()); })
})); .onErrorResume(e -> {
} catch (Exception e) { log.error("WVP 请求失败", e);
log.error("获取 WVP access token 失败", e);
// 即使获取 token 失败也继续转发请求
return chain.filter(exchange); return chain.filter(exchange);
} });
}; };
} }
/** /**
* 获取 access token使用缓存机制 * 调用 WVP 接口支持自动重试
* 如果缓存为空则调用 WvpTokenClient.login 获取新的 token *
* @param path 请求路径
* @param exchange ServerWebExchange
* @param retryCount 重试次数
* @return 响应体
*/
private Mono<String> callWvpWithRetry(String path, ServerWebExchange exchange, int retryCount) {
try {
String accessToken = getAccessToken();
String wvpUrl = "http://wvp-pro:18978" + path;
log.debug("调用 WVP 接口: {}, access-token: {}", wvpUrl, accessToken);
return webClient.get()
.uri(wvpUrl)
.header("access-token", accessToken)
.retrieve()
.bodyToMono(String.class)
.onErrorResume(e -> {
log.error("WVP 请求失败,尝试刷新 token 并重试", e);
if (retryCount < 1) {
// 清除缓存的 token下次会重新获取
clearCachedToken();
// 重试一次
return callWvpWithRetry(path, exchange, retryCount + 1);
}
return Mono.error(e);
});
} catch (Exception e) {
log.error("获取 access token 失败", e);
return Mono.error(e);
}
}
/**
* 获取 access token使用缓存机制并支持提前刷新
* 如果缓存为空或即将过期则调用 WvpTokenClient.login 获取新的 token
*/ */
private String getAccessToken() throws Exception { private String getAccessToken() throws Exception {
if (cachedAccessToken == null) { LocalDateTime now = LocalDateTime.now();
// 检查 token 是否需要刷新不存在或即将过期
if (cachedAccessToken == null || tokenExpirationTime == null ||
now.plusMinutes(REFRESH_BEFORE_EXPIRATION_MINUTES).isAfter(tokenExpirationTime)) {
synchronized (LOCK) { synchronized (LOCK) {
if (cachedAccessToken == null) { // 双重检查
log.info("缓存中没有 access token正在登录 WVP..."); now = LocalDateTime.now();
if (cachedAccessToken == null || tokenExpirationTime == null ||
now.plusMinutes(REFRESH_BEFORE_EXPIRATION_MINUTES).isAfter(tokenExpirationTime)) {
log.info("Token 不存在或即将过期,正在刷新 WVP access token...");
WvpTokenClient.LoginResponse loginResponse = WvpTokenClient.login("admin", "admin"); WvpTokenClient.LoginResponse loginResponse = WvpTokenClient.login("admin", "admin");
if (loginResponse.getCode() == 0 && loginResponse.getData() != null) { if (loginResponse.getCode() == 0 && loginResponse.getData() != null) {
cachedAccessToken = loginResponse.getData().getAccessToken(); cachedAccessToken = loginResponse.getData().getAccessToken();
log.info("WVP 登录成功,获取到 access token"); // 设置过期时间为当前时间 + TOKEN_EXPIRATION_MINUTES
tokenExpirationTime = now.plusMinutes(TOKEN_EXPIRATION_MINUTES);
log.info("WVP 登录成功,获取到 access token过期时间: {}", tokenExpirationTime);
} else { } else {
throw new Exception("WVP 登录失败: " + loginResponse.getMsg()); throw new Exception("WVP 登录失败: " + loginResponse.getMsg());
} }
} }
} }
} }
return cachedAccessToken; return cachedAccessToken;
} }
@ -85,6 +152,7 @@ public class WvpAccessTokenFilter extends AbstractGatewayFilterFactory<WvpAccess
public static void clearCachedToken() { public static void clearCachedToken() {
synchronized (LOCK) { synchronized (LOCK) {
cachedAccessToken = null; cachedAccessToken = null;
tokenExpirationTime = null;
log.info("已清除缓存的 WVP access token"); log.info("已清除缓存的 WVP access token");
} }
} }