You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Cloud Gateway路由权限配置及Angular跨域问题求助

问题:Spring Cloud Gateway路由认证与CORS配置异常

需求与问题

需要配置Spring Cloud Gateway实现以下功能:

  • /api/v1/auth/** 路由:验证Angular前端发送的OAuth2令牌
  • /api/v1/user/** 路由:无需认证,直接转发

当前存在两个异常:

  1. 两个路由均被要求认证,不符合预期;
  2. 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规则:

  1. 在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;
}
  1. 调整application.yml的Gateway CORS配置:
spring:
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/**]':
            allowedOrigins: "http://localhost:4200"
            allowedHeaders: "*"
            allowedMethods: "*"
            exposedHeaders: "*"
            allowCredentials: true
  1. 修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 20:45:53