Compare commits

...

3 Commits

Author SHA1 Message Date
litianxiang
f7e87fe84c 微服务改造 2026-04-22 15:54:43 +08:00
litianxiang
5284bfa813 微服务改造 2026-04-22 11:16:47 +08:00
litianxiang
218bed1813 微服务改造 2026-04-22 11:15:35 +08:00
6 changed files with 130 additions and 60 deletions

1
.gitignore vendored
View File

@@ -1 +1,2 @@
/.idea/
/target/

19
pom.xml
View File

@@ -99,6 +99,25 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Knife4j Gateway Aggregation -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-gateway-spring-boot-starter</artifactId>
<version>4.4.0</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-ui</artifactId>
<version>4.4.0</version>
</dependency>
<!-- Spring Cloud LoadBalancer -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
</dependencies>
<dependencyManagement>

View File

@@ -25,6 +25,7 @@ import reactor.core.publisher.Mono;
import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import org.springframework.util.AntPathMatcher;
import org.springframework.beans.factory.annotation.Qualifier;
/**
@@ -46,6 +47,7 @@ public class GlobalAuthWebFilter implements WebFilter {
@Qualifier("reactiveRedisTemplate")
private final ReactiveRedisTemplate<String, String> redisTemplate;
private final ObjectMapper objectMapper;
private final AntPathMatcher pathMatcher = new AntPathMatcher();
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
@@ -93,12 +95,21 @@ public class GlobalAuthWebFilter implements WebFilter {
// 5. 黑名单检查(仅当启用时)
if (authProperties.isBlacklistEnabled()) {
String blacklistKey = AuthConstants.BLACKLIST_PREFIX + principal.getId();
Boolean isBlacklisted = redisTemplate.hasKey(blacklistKey).block();
if (Boolean.TRUE.equals(isBlacklisted)) {
return writeUnauthorized(exchange, AuthConstants.MSG_TOKEN_BLACKLISTED);
}
return redisTemplate.hasKey(blacklistKey).flatMap(isBlacklisted -> {
if (Boolean.TRUE.equals(isBlacklisted)) {
return writeUnauthorized(exchange, AuthConstants.MSG_TOKEN_BLACKLISTED);
}
return continueChain(exchange, chain, principal);
}).onErrorResume(e -> {
log.error("Redis check failed", e);
return continueChain(exchange, chain, principal);
});
}
return continueChain(exchange, chain, principal);
}
private Mono<Void> continueChain(ServerWebExchange exchange, WebFilterChain chain, AuthPrincipalVo principal) {
// 6. 写入下游请求头
String userInfoJson;
try {
@@ -121,27 +132,13 @@ public class GlobalAuthWebFilter implements WebFilter {
return false;
}
for (String pattern : authProperties.getIgnorePaths()) {
if (matches(pattern, requestUri)) {
if (pathMatcher.match(pattern, requestUri)) {
return true;
}
}
return false;
}
private boolean matches(String pattern, String uri) {
if (pattern.endsWith("/**")) {
String prefix = pattern.substring(0, pattern.length() - 3);
return uri.startsWith(prefix);
}
if (pattern.endsWith("/*")) {
String prefix = pattern.substring(0, pattern.length() - 2);
if (!uri.startsWith(prefix)) return false;
String suffix = uri.substring(prefix.length());
return !suffix.contains("/");
}
return uri.contains(pattern);
}
private Claims parseToken(String token) {
SecretKey key = buildSigningKey();
return Jwts.parser()

View File

@@ -1,37 +0,0 @@
package com.aida.gateway.route;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 路由配置。
* <p>
* 注意:实际生产环境中建议将路由配置放在 Nacos 配置中心。
* StripPrefix=1 将 /seller 前缀剥离,例如:
* /seller/designer/check -> /designer/check (发到 aida-seller 的 /api/designer/check)
*/
@Configuration
public class RouteConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
// /internal/** 用于内部服务调用(如 logout 黑名单),不需要 stripPrefix
.route("aida-gateway-internal", r -> r
.path("/internal/**")
.uri("forward:/internal"))
// aida-seller 服务
.route("aida-seller", r -> r
.path("/seller/**")
.filters(f -> f.stripPrefix(1))
.uri("lb://aida-seller"))
// aida-back_001 服务
.route("aida-back", r -> r
.path("/api/**")
.filters(f -> f.stripPrefix(1))
.uri("lb://aida-back"))
.build();
}
}

View File

@@ -9,11 +9,47 @@ server:
spring:
application:
name: aida-gateway
cloud:
gateway:
routes:
- id: aida-back
uri: lb://aida-back
predicates:
- Path=/aida/**
filters:
- StripPrefix=1
- id: aida-seller
uri: lb://aida-seller
predicates:
- Path=/seller/**
filters:
- StripPrefix=1
- id: aida-python
uri: http://18.167.251.121:9994
predicates:
- Path=/python/**
# ---------- Knife4j 网关聚合配置 ----------
knife4j:
gateway:
enabled: true
# 手动指定下游服务的文档聚合,可以灵活应对不同服务的 API 版本或路径前缀
strategy: manual
routes:
- name: aida主服务
url: /aida/v3/api-docs
service-name: aida-back
context-path: /aida
order: 1
- name: 商家端服务 (Seller)
url: /seller/v3/api-docs
service-name: aida-seller
context-path: /seller
order: 2
# ---------- Gateway JWT 认证gateway 独有) ----------
gateway:
auth:
jwt-secret: ${BACK_JWT_SECRET:JWTSECRET}
jwt-secret: JWTSECRET
jwt-token-header: Authorization
jwt-token-prefix: Bearer-
blacklist-enabled: true
@@ -21,11 +57,15 @@ gateway:
- /favicon.ico
- /doc.html
- /swagger-ui.html
- /swagger-ui
- /swagger-ui/**
- /swagger-resources/**
- /v2/api-docs
- /v2/api-docs/**
- /v3/api-docs
- /v3/api-docs/**
- /webjars/**
- /**/v3/api-docs/**
- /api/account/login
- /api/account/preLogin
- /api/designer/check
@@ -54,6 +94,56 @@ gateway:
- /api/global-award/**
- /api/llm/stream
- /notification/**
- /aida/api/account/login
- /aida/api/account/preLogin
- /aida/api/designer/check
- /aida/api/account/sendEmail
- /aida/api/account/noLoginRequired
- /aida/api/account/resetPwd
- /aida/api/account/designWorksRegister
- /aida/api/account/questionnaire
- /aida/api/account/schoolLogin
- /aida/api/account/enterpriseLogin
- /aida/api/account/organizationNameSearch
- /aida/api/account/activateNewEmail
- /aida/api/python/saveGeneratePicture
- /aida/api/python/getLibraryByUserId
- /aida/api/python/flush
- /aida/api/account/healthy
- /aida/api/third/party/**
- /aida/api/element/initDefaultSysFile
- /aida/api/ali-pay/trade/notify
- /aida/api/paypal/ipn/back
- /aida/api/alipay-hk/trade/notify
- /aida/api/stripe/trade/notify
- /aida/api/portfolio/**
- /aida/api/global-award/**
- /aida/api/llm/stream
- /aida/account/login
- /aida/account/preLogin
- /aida/designer/check
- /aida/account/sendEmail
- /aida/account/noLoginRequired
- /aida/account/resetPwd
- /aida/account/designWorksRegister
- /aida/account/questionnaire
- /aida/account/schoolLogin
- /aida/account/enterpriseLogin
- /aida/account/organizationNameSearch
- /aida/account/activateNewEmail
- /aida/python/saveGeneratePicture
- /aida/python/getLibraryByUserId
- /aida/python/flush
- /aida/account/healthy
- /aida/third/party/**
- /aida/element/initDefaultSysFile
- /aida/ali-pay/trade/notify
- /aida/paypal/ipn/back
- /aida/alipay-hk/trade/notify
- /aida/stripe/trade/notify
- /aida/portfolio/**
- /aida/global-award/**
- /aida/llm/stream
logging:
level:

View File

@@ -8,13 +8,13 @@ spring:
application:
name: aida-gateway
config:
import: optional:nacos:aida-public-${NACOS_NAMESPACE:dev}.yml
import: optional:nacos:aida-public-${NACOS_NAMESPACE:test}.yml
cloud:
nacos:
discovery:
server-addr: ${NACOS_HOST:127.0.0.1:8848}
namespace: ${NACOS_NAMESPACE:dev}
namespace: ${NACOS_NAMESPACE:test}
config:
server-addr: ${NACOS_HOST:127.0.0.1:8848}
namespace: ${NACOS_NAMESPACE:dev}
namespace: ${NACOS_NAMESPACE:test}
file-extension: yaml