This commit is contained in:
2026-09-29 17:55:54 +08:00
parent 41d8387cd5
commit 54ad26c607
111 changed files with 1837 additions and 962 deletions
@@ -9,7 +9,7 @@ import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.oauth2.jose.jws.MacAlgorithm;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder;
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
import org.springframework.security.oauth2.jwt.JwtValidators;
@@ -17,8 +17,9 @@ import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.web.cors.reactive.CorsConfigurationSource;
import reactor.core.publisher.Mono;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
import org.springframework.security.oauth2.jwt.JwtClaimValidator;
import java.util.List;
import java.time.Instant;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -44,6 +45,9 @@ public class GatewaySecurityConfig {
.access(new AppScopeAuthorizationManager("MERCHANT"));
exchanges.pathMatchers("/api/platform/**")
.access(new AppScopeAuthorizationManager("PLATFORM"));
exchanges.pathMatchers("/api/pda/**")
.access(new AppScopeAuthorizationManager("PLATFORM"));
exchanges.pathMatchers("/internal/**").denyAll();
exchanges.anyExchange().authenticated();
return http.cors(cors -> cors.configurationSource(corsConfigurationSource))
.csrf(ServerHttpSecurity.CsrfSpec::disable)
@@ -59,12 +63,11 @@ public class GatewaySecurityConfig {
@Bean
ReactiveJwtDecoder reactiveJwtDecoder(GatewaySecurityProperties properties) {
byte[] key = properties.jwtSecret().getBytes(StandardCharsets.UTF_8);
if (key.length < 32) throw new IllegalStateException("JWT_SECRET 至少需要 32 字节");
NimbusReactiveJwtDecoder decoder = NimbusReactiveJwtDecoder
.withSecretKey(new SecretKeySpec(key, "HmacSHA256"))
.macAlgorithm(MacAlgorithm.HS256).build();
decoder.setJwtValidator(JwtValidators.createDefaultWithIssuer(properties.issuer()));
.withJwkSetUri(properties.jwkSetUri()).jwsAlgorithm(SignatureAlgorithm.RS256).build();
decoder.setJwtValidator(new DelegatingOAuth2TokenValidator<>(
JwtValidators.createDefaultWithIssuer(properties.issuer()),
new JwtClaimValidator<List<String>>("aud", values -> values != null && values.contains(properties.audience()))));
return decoder;
}
@@ -8,10 +8,11 @@ import java.util.List;
* 网关安全配置模型,集中承载 JWT 密钥、签发方、Redis 令牌键前缀和匿名放行路径。
*/
@ConfigurationProperties(prefix = "baiyue.security")
public record GatewaySecurityProperties(String jwtSecret, String issuer, String tokenKeyPrefix,
public record GatewaySecurityProperties(String jwkSetUri, String issuer, String audience, String tokenKeyPrefix,
List<String> permitPaths) {
public GatewaySecurityProperties {
issuer = issuer == null || issuer.isBlank() ? "auth-service" : issuer;
audience = audience == null || audience.isBlank() ? "lastleg-api" : audience;
tokenKeyPrefix = normalize(tokenKeyPrefix);
permitPaths = permitPaths == null ? List.of() : List.copyOf(permitPaths);
}
+29 -1
View File
@@ -42,6 +42,14 @@ spring:
enabled: false
lower-case-service-id: true
routes:
- id: lastleg-service
uri: lb://lastleg-service
predicates:
- Path=/api/merchant/v1/last-leg/**,/api/platform/v1/last-leg/**,/api/pda/v1/last-leg/**
- id: warehouse-overseas-service
uri: lb://warehouse-overseas-service
predicates:
- Path=/api/platform/v1/overseas-warehouse-resources/**
- id: auth-service
uri: lb://auth-service
predicates:
@@ -83,6 +91,18 @@ spring:
predicates:
- Path=/api/platform/finance/v1/**,/api/client/finance/v1/**
# Swagger 文档聚合路由。
- id: lastleg-openapi
uri: lb://lastleg-service
predicates:
- Path=/lastleg-service/v3/api-docs/**
filters:
- StripPrefix=1
- id: warehouse-overseas-openapi
uri: lb://warehouse-overseas-service
predicates:
- Path=/warehouse-overseas-service/v3/api-docs/**
filters:
- StripPrefix=1
- id: auth-openapi
uri: lb://auth-service
predicates:
@@ -158,7 +178,9 @@ baiyue:
allow-credentials: ${CORS_ALLOW_CREDENTIALS:false}
max-age: ${CORS_MAX_AGE:3600}
security:
jwt-secret: ${JWT_SECRET:change-me-to-at-least-32-bytes-long-before-start}
jwk-set-uri: ${JWT_JWKS_URI:http://auth-service:9100/api/auth/.well-known/jwks.json}
issuer: ${JWT_ISSUER:auth-service}
audience: ${JWT_AUDIENCE:lastleg-api}
token-key-prefix: ${REDIS_TOKEN_KEY_PREFIX:baiyue:auth}
permit-paths:
- /api/pda-updates/**
@@ -183,6 +205,8 @@ baiyue:
- /integration-service/v3/api-docs/**
- /event-bridge-service/v3/api-docs/**
- /finance-service/v3/api-docs/**
- /lastleg-service/v3/api-docs/**
- /warehouse-overseas-service/v3/api-docs/**
management:
endpoints:
@@ -231,3 +255,7 @@ springdoc:
url: /event-bridge-service/v3/api-docs
- name: finance
url: /finance-service/v3/api-docs
- name: lastleg
url: /lastleg-service/v3/api-docs
- name: warehouse-overseas
url: /warehouse-overseas-service/v3/api-docs