From 58951ff9b6a93cde8d58919f6676da7a5dc23a6c Mon Sep 17 00:00:00 2001 From: litianxiang Date: Mon, 11 May 2026 16:40:45 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B9=B0=E5=AE=B6=E7=AB=AF=E9=9C=80=E8=A6=81?= =?UTF-8?q?=E7=9A=84=E8=8E=B7=E5=8F=96=E5=95=86=E5=AE=B6=E4=B8=BB=E9=A1=B5?= =?UTF-8?q?=E5=92=8C=E6=A8=A1=E7=B3=8A=E6=90=9C=E7=B4=A2=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gateway/config/GatewayAuthProperties.java | 8 ++++ .../gateway/filter/GlobalAuthWebFilter.java | 41 ++++++++++++++++--- src/main/resources/application.yml | 4 ++ 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/aida/gateway/config/GatewayAuthProperties.java b/src/main/java/com/aida/gateway/config/GatewayAuthProperties.java index 28e2f5e..f700ba0 100644 --- a/src/main/java/com/aida/gateway/config/GatewayAuthProperties.java +++ b/src/main/java/com/aida/gateway/config/GatewayAuthProperties.java @@ -19,5 +19,13 @@ public class GatewayAuthProperties { private List ignorePaths; + /** + * 可选认证路径:token 有则解析并写入下游请求头,无则放行。 + * 与 ignorePaths 的区别:ignorePaths 完全跳过认证逻辑; + * optionalAuthPaths 仍然尝试解析 token,有 token 时正常写入 X-User-Id / X-User-Info, + * 无 token 时才放行,确保已登录用户的信息能正确传递。 + */ + private List optionalAuthPaths; + private boolean blacklistEnabled = true; } diff --git a/src/main/java/com/aida/gateway/filter/GlobalAuthWebFilter.java b/src/main/java/com/aida/gateway/filter/GlobalAuthWebFilter.java index 8ee0352..83f0f77 100644 --- a/src/main/java/com/aida/gateway/filter/GlobalAuthWebFilter.java +++ b/src/main/java/com/aida/gateway/filter/GlobalAuthWebFilter.java @@ -65,24 +65,43 @@ public class GlobalAuthWebFilter implements WebFilter, Ordered { return chain.filter(exchange); } - // 2. 白名单直接放行 + // 2. 白名单直接放行(完全跳过认证) if (isIgnoredPath(path)) { return chain.filter(exchange); } - // 3. 提取 token + // 3. 可选认证路径:token 有则解析,无则放行 + if (isOptionalAuthPath(path)) { + String rawHeader = exchange.getRequest().getHeaders() + .getFirst(authProperties.getJwtTokenHeader()); + if (StrUtil.isBlank(rawHeader)) { + // 无 token,直接放行,不写任何 header + return chain.filter(exchange); + } + // 有 token,正常走解析流程(复用下面的验证逻辑) + return processTokenWithAuthCheck(exchange, chain, rawHeader); + } + + // 4. 其他路径:必须有 token String rawHeader = exchange.getRequest().getHeaders() .getFirst(authProperties.getJwtTokenHeader()); if (StrUtil.isBlank(rawHeader)) { return writeUnauthorized(exchange, AuthConstants.MSG_MISSING_TOKEN); } + return processTokenWithAuthCheck(exchange, chain, rawHeader); + } + /** + * 统一的 token 解析与认证流程:解析 JWT → 黑名单检查 → 写入下游 header。 + * 专供可选认证路径中有 token 的情况,以及普通路径的鉴权。 + */ + private Mono processTokenWithAuthCheck(ServerWebExchange exchange, WebFilterChain chain, String rawHeader) { String token = rawHeader; if (rawHeader.startsWith(authProperties.getJwtTokenPrefix())) { token = rawHeader.substring(authProperties.getJwtTokenPrefix().length()); } - // 4. JWT 签名验证 + // JWT 签名验证 Claims claims; try { claims = parseToken(token); @@ -91,7 +110,7 @@ public class GlobalAuthWebFilter implements WebFilter, Ordered { return writeUnauthorized(exchange, AuthConstants.MSG_TOKEN_EXPIRED); } - // 5. 解析用户信息 + // 解析用户信息 AuthPrincipalVo principal; try { principal = objectMapper.readValue(claims.getSubject(), AuthPrincipalVo.class); @@ -104,7 +123,7 @@ public class GlobalAuthWebFilter implements WebFilter, Ordered { return writeUnauthorized(exchange, AuthConstants.MSG_INVALID_TOKEN); } - // 6. 黑名单检查(仅当启用时) + // 黑名单检查 if (authProperties.isBlacklistEnabled()) { String blacklistKey = AuthConstants.BLACKLIST_PREFIX + principal.getId(); return redisTemplate.hasKey(blacklistKey).flatMap(isBlacklisted -> { @@ -151,6 +170,18 @@ public class GlobalAuthWebFilter implements WebFilter, Ordered { return false; } + private boolean isOptionalAuthPath(String requestUri) { + if (authProperties.getOptionalAuthPaths() == null) { + return false; + } + for (String pattern : authProperties.getOptionalAuthPaths()) { + if (pathMatcher.match(pattern, requestUri)) { + return true; + } + } + return false; + } + private Claims parseToken(String token) { SecretKey key = buildSigningKey(); return Jwts.parser() diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index ff4f039..87cdb0c 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -75,6 +75,9 @@ gateway: jwt-token-header: Authorization jwt-token-prefix: Bearer- blacklist-enabled: true + # 可选认证路径:token 有则解析并传递,无则放行(已登录用户身份仍能正确传递) + optional-auth-paths: + - /seller/listing/shop ignore-paths: # Static resources & docs - /favicon.ico @@ -108,6 +111,7 @@ gateway: - /gateway/healthy # Designer - /aida/api/designer/check + - /seller/designer/shop/** # Python (only /aida prefix) - /aida/api/python/saveGeneratePicture - /aida/api/python/getLibraryByUserId