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规则配置正确,浏览器会自动处理重定向。强行添加头信息反而可能导致冲突。
修复步骤总结
- 调整网关路由顺序,将
backend_route和auth_route放在frontend路由之前,或给frontend路由添加排除规则。 - 移除网关
default-filters中的所有CORS头添加规则,保留CorsWebFilter或spring.web.cors其中一种配置。 - 删除原Spring Boot应用中的
MyCORSFilter。 - 检查Auth0控制台配置,确保网关回调地址、前端地址都在允许列表中。
- 重启网关和后端应用,验证已登录状态下的重定向请求是否正常。
内容的提问来源于stack exchange,提问作者learningProgrammer
相关产品推荐
相关产品推荐

