From 3ad0509eb0a6f56e3b74b0e924b6a16ae8fd2c6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=99=E5=B0=8F=E4=BA=91?= Date: Mon, 2 Feb 2026 13:19:15 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0wvp=E8=BF=87=E6=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gateway/filter/WvpAccessTokenFilter.java | 97 ++++++++++++ .../ruoyi/gateway/utils/WvpTokenClient.java | 149 ++++++++++++++++++ 2 files changed, 246 insertions(+) create mode 100644 src/main/java/com/ruoyi/gateway/filter/WvpAccessTokenFilter.java create mode 100644 src/main/java/com/ruoyi/gateway/utils/WvpTokenClient.java diff --git a/src/main/java/com/ruoyi/gateway/filter/WvpAccessTokenFilter.java b/src/main/java/com/ruoyi/gateway/filter/WvpAccessTokenFilter.java new file mode 100644 index 0000000..ccbfe05 --- /dev/null +++ b/src/main/java/com/ruoyi/gateway/filter/WvpAccessTokenFilter.java @@ -0,0 +1,97 @@ +package com.ruoyi.gateway.filter; + +import com.ruoyi.gateway.utils.WvpTokenClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.gateway.filter.GatewayFilter; +import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +/** + * WVP Access Token 过滤器 + * 用于为 WVP 请求添加或替换 access-token 请求头 + */ +@Component +public class WvpAccessTokenFilter extends AbstractGatewayFilterFactory { + + private static final Logger log = LoggerFactory.getLogger(WvpAccessTokenFilter.class); + + private static String cachedAccessToken = null; + private static final Object LOCK = new Object(); + + public WvpAccessTokenFilter() { + super(Config.class); + } + + @Override + public GatewayFilter apply(Config config) { + return (exchange, chain) -> { + try { + // 获取 access token + String accessToken = getAccessToken(); + + log.info("WVP 请求 - 原始路径: {}", exchange.getRequest().getURI().getPath()); + log.info("WVP 请求 - 添加 access-token"); + + // 修改请求,添加或替换 access-token 请求头 + ServerHttpRequest modifiedRequest = exchange.getRequest().mutate() + .header("access-token", accessToken) + .build(); + + // 创建新的 exchange + ServerWebExchange modifiedExchange = exchange.mutate() + .request(modifiedRequest) + .build(); + + return chain.filter(modifiedExchange).then(Mono.fromRunnable(() -> { + log.info("WVP 响应 - Status: {}", modifiedExchange.getResponse().getStatusCode()); + })); + } catch (Exception e) { + log.error("获取 WVP access token 失败", e); + // 即使获取 token 失败,也继续转发请求 + return chain.filter(exchange); + } + }; + } + + /** + * 获取 access token,使用缓存机制 + * 如果缓存为空,则调用 WvpTokenClient.login 获取新的 token + */ + private String getAccessToken() throws Exception { + if (cachedAccessToken == null) { + synchronized (LOCK) { + if (cachedAccessToken == null) { + log.info("缓存中没有 access token,正在登录 WVP..."); + WvpTokenClient.LoginResponse loginResponse = WvpTokenClient.login("admin", "admin"); + if (loginResponse.getCode() == 0 && loginResponse.getData() != null) { + cachedAccessToken = loginResponse.getData().getAccessToken(); + log.info("WVP 登录成功,获取到 access token"); + } else { + throw new Exception("WVP 登录失败: " + loginResponse.getMsg()); + } + } + } + } + return cachedAccessToken; + } + + /** + * 清除缓存的 access token(可用于 token 过期时重新获取) + */ + public static void clearCachedToken() { + synchronized (LOCK) { + cachedAccessToken = null; + log.info("已清除缓存的 WVP access token"); + } + } + + /** + * 配置类 + */ + public static class Config { + } +} \ No newline at end of file diff --git a/src/main/java/com/ruoyi/gateway/utils/WvpTokenClient.java b/src/main/java/com/ruoyi/gateway/utils/WvpTokenClient.java new file mode 100644 index 0000000..6a9d0d7 --- /dev/null +++ b/src/main/java/com/ruoyi/gateway/utils/WvpTokenClient.java @@ -0,0 +1,149 @@ +package com.ruoyi.gateway.utils; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; + +/** + * WVP Token 客户端 + * 用于获取 WVP-PRO 的 access token + */ +public class WvpTokenClient { + + private static final Logger log = LoggerFactory.getLogger(WvpTokenClient.class); + + // WVP 服务地址 - 使用容器名称进行容器间通信 + private static final String WVP_BASE_URL = "http://wvp-pro:18978"; + + private static final ObjectMapper objectMapper = new ObjectMapper(); + + /** + * WVP登录响应对象 + */ + public static class LoginResponse { + private int code; + private String msg; + private LoginData data; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public LoginData getData() { + return data; + } + + public void setData(LoginData data) { + this.data = data; + } + } + + /** + * 登录数据对象 + */ + public static class LoginData { + private String accessToken; + private String username; + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + } + + /** + * 计算MD5哈希值 + * + * @param input 输入字符串 + * @return MD5哈希值(小写) + * @throws Exception 如果计算失败 + */ + private static String md5(String input) throws Exception { + MessageDigest md = MessageDigest.getInstance("MD5"); + byte[] messageDigest = md.digest(input.getBytes(StandardCharsets.UTF_8)); + StringBuilder hexString = new StringBuilder(); + for (byte b : messageDigest) { + String hex = Integer.toHexString(0xff & b); + if (hex.length() == 1) { + hexString.append('0'); + } + hexString.append(hex); + } + return hexString.toString(); + } + + /** + * WVP用户登录 + * + * @param username 用户名 + * @param password 密码(明文) + * @return 登录响应对象 + * @throws Exception 如果请求失败 + */ + public static LoginResponse login(String username, String password) throws Exception { + // 计算密码的MD5值 + String passwordMd5 = md5(password); + + // 构建 URL 参数 + StringBuilder urlBuilder = new StringBuilder(WVP_BASE_URL); + urlBuilder.append("/api/user/login?"); + urlBuilder.append("username=").append(URLEncoder.encode(username, StandardCharsets.UTF_8.name())); + urlBuilder.append("&password=").append(passwordMd5); + + // 创建 HTTP 连接 + URL url = new URL(urlBuilder.toString()); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); + connection.setConnectTimeout(10000); + connection.setReadTimeout(10000); + + // 获取响应 + int responseCode = connection.getResponseCode(); + if (responseCode == HttpURLConnection.HTTP_OK) { + BufferedReader reader = new BufferedReader( + new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8)); + StringBuilder response = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + response.append(line); + } + reader.close(); + + // 解析JSON响应 + return objectMapper.readValue(response.toString(), LoginResponse.class); + } else { + throw new Exception("HTTP 请求失败,状态码: " + responseCode); + } + } +} \ No newline at end of file