Spring Cloud Gateway路由权限配置及Angular跨域问题求助
问题:Spring Cloud Gateway路由认证与CORS配置异常
需求与问题
需要配置Spring Cloud Gateway实现以下功能:
/api/v1/auth/**路由:验证Angular前端发送的OAuth2令牌/api/v1/user/**路由:无需认证,直接转发
当前存在两个异常:
- 两个路由均被要求认证,不符合预期;
- Postman携带令牌请求正常,但Angular前端请求触发CORS错误。
API Gateway控制台日志
2024-04-03T12:20:59.008+02:00 DEBUG 4188 --- [api-gateway] [ctor-http-nio-2] .s.s.w.s.u.m.AndServerWebExchangeMatcher : Trying to match using org.springframework.security.web.server.csrf.CsrfWebFilter$DefaultRequireCsrfProtectionMatcher@39c0c881 2024-04-03T12:20:59.008+02:00 DEBUG 4188 --- [api-gateway] [ctor-http-nio-2] .s.s.w.s.u.m.AndServerWebExchangeMatcher : Did not match 2024-04-03T12:20:59.013+02:00 DEBUG 4188 --- [api-gateway] [ parallel-1] o.s.s.w.s.u.m.OrServerWebExchangeMatcher : Trying to match using PathMatcherServerWebExchangeMatcher{pattern='/logout', method=POST} 2024-04-03T12:20:59.014+02:00 DEBUG 4188 --- [api-gateway] [ parallel-1] athPatternParserServerWebExchangeMatcher : Request 'OPTIONS /api/v1/user/1' doesn't match 'POST /logout' 2024-04-03T12:20:59.014+02:00 DEBUG 4188 --- [api-gateway] [ parallel-1] o.s.s.w.s.u.m.OrServerWebExchangeMatcher : No matches found 2024-04-03T12:20:59.015+02:00 DEBUG 4188 --- [api-gateway] [ parallel-1] a.DelegatingReactiveAuthorizationManager : Checking authorization on '/api/v1/user/1' using org.springframework.security.authorization.AuthenticatedReactiveAuthorizationManager@7967ad6a 2024-04-03T12:20:59.016+02:00 DEBUG 4188 --- [api-gateway] [ parallel-1] ebSessionServerSecurityContextRepository : No SecurityContext found in WebSession: 'org.springframework.web.server.session.InMemoryWebSessionStore$InMemoryWebSession@54f86865' 2024-04-03T12:20:59.016+02:00 DEBUG 4188 --- [api-gateway] [ parallel-1] o.s.s.w.s.a.AuthorizationWebFilter : Authorization failed: Access Denied 2024-04-03T12:20:59.018+02:00 DEBUG 4188 --- [api-gateway] [ parallel-1] ebSessionServerSecurityContextRepository : No SecurityContext found in WebSession: 'org.springframework.web.server.session.InMemoryWebSessionStore$InMemoryWebSession@54f86865'
浏览器终端错误
Access to fetch at 'http***' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
API Gateway配置信息
POM文件
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.2.4</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.vortex</groupId> <artifactId>api-gateway</artifactId> <version>0.0.1-SNAPSHOT</version> <name>api-gateway</name> <description>The gateway for Vortex Project</description> <properties> <java.version>17</java.version> <spring-cloud.version>2023.0.0</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>io.projectreactor</groupId> <artifactId>reactor-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-resource-server</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
application.yml文件
#logs logging: level: org.springframework.security: DEBUG org: springframework: cloud: gateway: DEBUG web: reactive: DEBUG reactor: netty: DEBUG oauth2: client: DEBUG # API Gateway Configuration server: port: 8080 spring: application: name: api-gateway cloud: gateway: # Routes Configuration routes: - id: auth-cognito uri: http://localhost:8083 predicates: - Path=/api/v1/auth/** - Method=POST,GET,PUT,DELETE,OPTIONS - id: user-service uri: http://localhost:8084 predicates: - Path=/api/v1/user/** - Method=POST,GET,PUT,DELETE,OPTIONS # Global Filters Configuration default-filters: - DedupeResponseHeader=Access-Control-Allow-Origin Access-Control-Allow-Credentials, RETAIN_UNIQUE # Global CORS Configuration globalcors: cors-configurations: '[/**]': allowedOrigins: "*" allowedHeaders: "*" allowedMethods: "*" exposedHeaders: "*" security: oauth2: resourceserver: jwt: issuer-uri: cognito jwk-set-uri: cognito
Angular代码
HttpInterceptorFn
import { HttpInterceptorFn } from '@angular/common/http'; import { inject } from '@angular/core'; import { CognitoService } from '../services/cognito.service'; export const authInterceptor: HttpInterceptorFn = (req, next) => { const cognitoService = inject(CognitoService); const token = cognitoService.token; const authReq = req.clone({ setHeaders: { Authorization: `Bearer ${token}` } }); return next(authReq); };
appConfig
export const appConfig: ApplicationConfig = { providers: [ provideRouter(routes), provideClientHydration(), provideHttpClient( withInterceptors([authInterceptor]), withFetch(), ) ] };
解决方案
一、修复路由认证问题
当前Spring Security默认对所有请求启用认证,需通过SecurityWebFilterChain精准控制认证规则:
创建Spring Security配置类:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.web.server.SecurityWebFilterChain; @Configuration @EnableWebFluxSecurity public class SecurityConfig { @Bean public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) { http .authorizeExchange(exchanges -> exchanges .pathMatchers("/api/v1/user/**").permitAll() // 放行user路径 .pathMatchers("/api/v1/auth/**").authenticated() // auth路径需认证 .anyExchange().authenticated() ) .oauth2ResourceServer(oauth2 -> oauth2 .jwt(jwt -> jwt .issuerUri("你的Cognito issuer URI") // 替换为实际地址 .jwkSetUri("你的Cognito jwk地址") // 替换为实际地址 ) ) .csrf(csrf -> csrf.disable()); // Angular场景下可关闭CSRF return http.build(); } }
注意:确保issuer-uri和jwk-set-uri配置为Cognito的实际地址,可删除application.yml中对应的重复配置。
二、修复CORS问题
预请求(OPTIONS)被Spring Security拦截导致CORS头缺失,需同时配置Security和Gateway的CORS规则:
- 在SecurityConfig中添加CORS支持:
import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.reactive.CorsConfigurationSource; import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource; // 在securityWebFilterChain方法中添加 http.cors(cors -> cors.configurationSource(corsConfigurationSource())); // 新增CORS配置源Bean @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); configuration.addAllowedOrigin("http://localhost:4200"); // 明确允许前端源 configuration.addAllowedHeader("*"); configuration.addAllowedMethod("*"); configuration.setAllowCredentials(true); // 支持携带凭证 configuration.addExposedHeader("*"); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; }
- 调整application.yml的Gateway CORS配置:
spring: cloud: gateway: globalcors: cors-configurations: '[/**]': allowedOrigins: "http://localhost:4200" allowedHeaders: "*" allowedMethods: "*" exposedHeaders: "*" allowCredentials: true
- 修改Angular拦截器,跳过OPTIONS请求的Authorization头:
export const authInterceptor: HttpInterceptorFn = (req, next) => { const cognitoService = inject(CognitoService); const token = cognitoService.token; // 预请求不携带Authorization头 if (req.method === 'OPTIONS') { return next(req); } const authReq = req.clone({ setHeaders: { Authorization: `Bearer ${token}` } }); return next(authReq); };
内容的提问来源于stack exchange,提问作者user23981540
相关产品推荐
相关产品推荐

