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

Angular集成Spring Boot+Auth0登录时的CORS问题排查

关于Spring Boot+Auth0+Angular+Spring Cloud Gateway的CORS问题排查与解决

问题背景

  • 集成Auth0登录的Spring Boot应用,直接通过浏览器访问时登录功能正常;接入Angular前端后出现CORS问题:未登录时点击重定向链接可正常跳转至登录页,已登录状态下点击该链接则触发CORS报错
  • 已在Auth0中配置允许前端应用的CORS规则,跨机器部署后端后问题仍存在;观察到服务器重定向响应的Access-Control-Allow-Origin头配置正确,但怀疑该头未传递到重定向请求中
  • 疑问:前后端均使用localhost是否会引发问题?是否存在配置遗漏?是否需要在Angular前端添加拦截器手动处理重定向请求的头信息?

现有配置

原Spring Boot应用配置

CORS过滤器代码

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class MyCORSFilter implements Filter {

@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
        throws IOException, ServletException {

    HttpServletRequest request = (HttpServletRequest) req;
    HttpServletResponse response = (HttpServletResponse) res;
    response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
    response.setHeader("Access-Control-Allow-Credentials", "true");
    response.setHeader("Access-Control-Allow-Methods", "POST, GET, DELETE, OPTIONS");
    response.setHeader("Access-Control-Max-Age", "3600");
    response.setHeader("Access-Control-Allow-Headers",
            "Content-Type, Accept, X-Requested-With, remember-me");
    chain.doFilter(req, res);
}

@Override
public void init(FilterConfig filterConfig) {
}

@Override
public void destroy() {
}

}

application.yaml Auth0配置

spring:
  security:
   oauth2:
     client:
        registration:
          auth0:
            client-id: {ClientId}
            client-secret: {ClientSecret}
            scope: openid+profile+email
        provider:
          auth0:
            issuer-uri: {IssuerURI}

Spring Cloud Gateway配置

网关application.yaml

server:
  port: 8096
spring:
   application:
      name: bff
   web:
    cors:
      allowed-origins: "http://localhost:4200"
      allowed-methods: GET,POST,PUT,DELETE,OPTIONS
      allowed-headers: Authorization,Content-Type
      allow-credentials: true 
      max-age: 3600
   cloud:
     gateway:
       routes:
         - id: frontend
           uri: http://localhost:4200
           predicates:
             - Path=/**
           filters:
             - RewritePath=/(?<segment>.*), /${segment}
         - id: backend
            uri: http://localhost:8090
           predicates:
             - Path=/api/**
           filters:
             - StripPrefix=1
           default-filters:
               - AddRequestHeader=Access-Control-Allow-Origin,http://localhost:4200
               - AddResponseHeader=Access-Control-Allow-Origin,http://localhost:4200
               - AddResponseHeader=Access-Control-Allow-Methods,GET,POST,PUT,DELETE,OPTIONS
               - AddResponseHeader=Access-Control-Allow-Headers,Authorization,Content-Type
               - AddResponseHeader=Access-Control-Allow-Credentials,true
   security:
     oauth2:
       client:
         registration:
           auth0:
             client-id: {id}
             client-secret: {secret}
             scope: openid+profile+email
             redirect-uri: {redirect-uri}
             authorization-grant-type: authorization_code
             client-authentication-method: basic
         provider:
           auth0:
             issuer-uri: {issuer}
             jwk-set-uri: {issuer-json}
             user-info-uri: {user-issuer}
       resourceserver:
         jwt:
         issuer-uri: {issuer}
         jws-algorithms:
            - RS512

CorsConfig类

@Configuration
public class CorsConfig {

   @Bean
   public CorsWebFilter corsWebFilter() {
       CorsConfiguration corsConfiguration = new CorsConfiguration();
       corsConfiguration.addAllowedOrigin("http://localhost:4200");
       corsConfiguration.addAllowedMethod("*");
       corsConfiguration.addAllowedHeader("*");
       corsConfiguration.setAllowCredentials(true);

       UrlBasedCorsConfigurationSource source = new 
       UrlBasedCorsConfigurationSource();
       source.registerCorsConfiguration("/**", corsConfiguration);

       return new CorsWebFilter(source);
    }
 }

GatewayConfig类

@Configuration
public class GatewayConfig {

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) 
    {
        return builder.routes()
              .route("backend_route", r -> r.path("/api/**")
                    .uri("http://localhost:8090"))  // Backend API URI
            .route("auth_route", r -> r.path("/login/oauth2/**")
                    .uri({issure})) 
            .build();
     }
  }

SecurityConfig类

@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {

   @Bean
   public SecurityWebFilterChain 
   securityWebFilterChain(ServerHttpSecurity http) {
       http
           .authorizeExchange(exchanges -> exchanges
               .pathMatchers("/api/**").permitAll()
               .anyExchange().authenticated()
           )
           .oauth2Login(Customizer.withDefaults())
           .oauth2ResourceServer(oauth2 -> oauth2
               .jwt(jwt -> 
         jwt.jwtAuthenticationConverter(jwtAuthenticationConverter()))
        )
        .csrf(csrf -> csrf.disable()); 
    return http.build();
   }

@Bean
public JwtDecoder jwtDecoder() {
    return NimbusJwtDecoder.withJwkSetUri({issure-json}).build();
}


@Bean
public ReactiveJwtAuthenticationConverterAdapter 
 jwtAuthenticationConverter() {
    JwtAuthenticationConverter jwtAuthenticationConverter = new 
  JwtAuthenticationConverter();
    jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwt -> 
  {
        Collection<GrantedAuthority> authorities = 
    jwt.getClaimAsStringList("roles").stream()
            .map(role -> new SimpleGrantedAuthority("ROLE_" + role))   
            .collect(Collectors.toList());
        return authorities;
    });
    return new 
  ReactiveJwtAuthenticationConverterAdapter(jwtAuthenticationConverter);
} 

配置错误与遗漏排查

1. CORS配置冲突

网关同时配置了spring.web.cors、CorsWebFilter Bean和default-filters中的CORS响应头,多重配置会导致头信息重复或规则冲突,浏览器会因为不一致的CORS头触发报错。

  • 解决方案:保留一种CORS配置方式,推荐删除default-filters中的所有CORS头添加规则,只保留CorsWebFilter或spring.web.cors其中一种配置。

2. 重定向请求的Auth0配置遗漏

已登录状态下的重定向请求,Auth0的回调地址必须完整配置在Auth0控制台的Allowed Callback URLs、Allowed Web Origins、Allowed Logout URLs中,包括网关的回调地址(例如http://localhost:8096/login/oauth2/code/auth0)。

3. 网关路由优先级错误

网关中frontend路由的Path=/**会匹配所有请求,导致后续的backend_route和auth_route无法生效。

  • 解决方案:调整路由顺序,将更具体的路由(backend_route、auth_route)放在frontend路由前面;或者给frontend路由添加排除规则:
    - id: frontend
      uri: http://localhost:4200
      predicates:
        - Path=/**
        - Path!=/api/**
        - Path!=/login/oauth2/**
      filters:
        - RewritePath=/(?<segment>.*), /${segment}
    

4. 原Spring Boot应用CORS过滤器冗余

现在请求通过网关转发,原Spring Boot应用的MyCORSFilter会重复设置CORS头,导致冲突。

  • 解决方案:删除原Spring Boot应用中的MyCORSFilter,统一由网关处理CORS规则。

5. 前后端localhost的影响

部分浏览器对localhost的跨域处理有特殊限制,但跨机器部署后仍出现问题,说明核心原因不是localhost本身,而是配置冲突或重定向规则问题。可尝试修改Angular启动端口(如改为4201)验证,但优先解决上述配置问题。

6. Angular前端无需添加拦截器

不需要手动添加拦截器处理重定向请求,只要后端/网关的CORS规则配置正确,浏览器会自动处理重定向。强行添加头信息反而可能导致冲突。


修复步骤总结

  1. 调整网关路由顺序,将backend_route和auth_route放在frontend路由之前,或给frontend路由添加排除规则。
  2. 移除网关default-filters中的所有CORS头添加规则,保留CorsWebFilter或spring.web.cors其中一种配置。
  3. 删除原Spring Boot应用中的MyCORSFilter。
  4. 检查Auth0控制台配置,确保网关回调地址、前端地址都在允许列表中。
  5. 重启网关和后端应用,验证已登录状态下的重定向请求是否正常。

内容的提问来源于stack exchange,提问作者learningProgrammer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 11:14:58