已配置白名单端口仍遭CORS拦截?Spring Boot跨域问题求助
前端运行在4200端口,调用Spring Boot 3.1后端的http://localhost:8080/writer/create接口时,收到CORS拦截错误:
Access to fetch at 'http://localhost:8080/writer/create' 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.
已在后端配置SecurityConfig类(包含CorsFilter,将http://localhost:4200加入允许源白名单),代码如下:
@Configuration @EnableWebSecurity @RequiredArgsConstructor public class SecurityConfig { private final JwtAuthenticationFilter jwtAuthFilter; private final AuthenticationProvider authenticationProvider; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf(csrf -> csrf.disable()) .authorizeRequests() .requestMatchers("api/v1/auth/**","reader/**") .permitAll() .requestMatchers("/writer/**").hasAnyAuthority("WRITER") .anyRequest() .authenticated() .and() .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authenticationProvider(authenticationProvider) .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } @Bean public CorsFilter corsFilter() { CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.setAllowCredentials(true); corsConfiguration.setAllowedOrigins(Collections.singletonList("http://localhost:4200")); corsConfiguration.setAllowedHeaders(Arrays.asList("Origin", "Access-Control-Allow-Origin", "Content-Type", "Accept", "Authorization", "Origin, Accept", "X-Requested-With", "Access-Control-Request-Method", "Access-Control-Request-Headers")); corsConfiguration.setExposedHeaders(Arrays.asList("Origin", "Content-Type", "Accept", "Authorization", "Access-Control-Allow-Origin", "Access-Control-Allow-Origin", "Access-Control-Allow-Credentials")); corsConfiguration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource(); urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration); return new CorsFilter(urlBasedCorsConfigurationSource); } }
为何4200端口仍被拦截?如何实现前后端正常调用?
问题核心是过滤器执行顺序冲突:自定义的CorsFilter默认在Spring Security过滤器链之后执行,而/writer/**接口需要WRITER权限,OPTIONS预检请求未携带JWT令牌,会被Security的权限校验直接拦截,根本没机会走到CorsFilter处理,导致响应头缺失Access-Control-Allow-Origin。
另外,Spring Boot 3.x官方推荐直接通过HttpSecurity集成CORS配置,而非单独注册CorsFilter,这样能确保CORS逻辑在权限校验前执行。
修改SecurityConfig,将CORS配置直接集成到Security过滤器链中,替换原有独立的CorsFilter Bean:
@Configuration @EnableWebSecurity @RequiredArgsConstructor public class SecurityConfig { private final JwtAuthenticationFilter jwtAuthFilter; private final AuthenticationProvider authenticationProvider; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf(csrf -> csrf.disable()) // 集成CORS配置到Security链 .cors(cors -> cors.configurationSource(corsConfigurationSource())) .authorizeRequests() .requestMatchers("api/v1/auth/**","reader/**") .permitAll() // 允许所有OPTIONS预检请求直接通过,无需权限校验 .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll() .requestMatchers("/writer/**").hasAnyAuthority("WRITER") .anyRequest() .authenticated() .and() .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authenticationProvider(authenticationProvider) .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } // 定义CORS配置源 private CorsConfigurationSource corsConfigurationSource() { CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.setAllowCredentials(true); corsConfiguration.setAllowedOrigins(Collections.singletonList("http://localhost:4200")); // 清理冗余的响应头配置,只保留请求允许的头 corsConfiguration.setAllowedHeaders(Arrays.asList("Origin", "Content-Type", "Accept", "Authorization", "X-Requested-With", "Access-Control-Request-Method", "Access-Control-Request-Headers")); corsConfiguration.setExposedHeaders(Arrays.asList("Origin", "Content-Type", "Accept", "Authorization", "Access-Control-Allow-Credentials")); corsConfiguration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", corsConfiguration); return source; } }
关键修改说明
- 通过
http.cors()将CORS逻辑嵌入Security过滤器链,确保CORS处理在权限校验之前完成 - 添加
requestMatchers(HttpMethod.OPTIONS, "/**").permitAll(),放行所有OPTIONS预检请求,避免无令牌被拦截 - 清理了
AllowedHeaders中重复的Access-Control-Allow-Origin(该字段是响应头,无需配置在请求允许头列表)
额外检查项
- 若前端需要传递Cookie或认证信息,确保请求携带
withCredentials: true参数 - 重启后端服务,确保配置生效
内容的提问来源于stack exchange,提问作者Tanabata Cinco

