Spring Boot带认证应用出现CORS错误,求排查SecurityFilterChain问题
问题根源分析
你的问题核心是Spring Security拦截了跨域预检的OPTIONS请求:
- 无需认证的
/api/v1/auth/**接口因为配置了permitAll,OPTIONS预检请求能直接通过,WebMvc的CORS配置可以正常给响应添加跨域头; - 但需要认证的接口,OPTIONS请求不会携带JWT令牌,被Spring Security的认证逻辑拦截,响应里没有返回CORS头,导致浏览器触发跨域错误。
解决方案
在Spring Security配置里显式开启CORS支持,让它统一处理跨域规则,同时确保预检请求不被拦截。修改后的完整配置代码如下:
@Configuration @EnableWebSecurity @RequiredArgsConstructor @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfiguration { private final JwtAuthenticationFilter jwtAuthFilter; private final AuthenticationProvider authenticationProvider; @Bean public SecurityFilterChain getSecurityFilterChain(HttpSecurity http) throws Exception { http.csrf() .disable() // 关键:开启Spring Security的CORS支持,会自动绑定下面的CORS配置 .cors(Customizer.withDefaults()) .authorizeHttpRequests() .requestMatchers(new AntPathRequestMatcher("/api/v1/auth/**")) .permitAll() // 兜底允许所有OPTIONS预检请求直接通过 .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll() .anyRequest() .authenticated() .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authenticationProvider(authenticationProvider) .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } // 用Spring Security的CorsConfigurationSource替换原WebMvc的CORS配置,统一管理 @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); // 开发环境指定具体前端域名,生产环境不要用*,更安全 configuration.setAllowedOrigins(Collections.singletonList("http://localhost:3000")); configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS")); // 明确允许携带Authorization头(JWT令牌放在这个头里) configuration.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type")); configuration.setAllowCredentials(false); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; } }
额外注意事项
如果修改后仍有问题,检查你的JwtAuthenticationFilter是否拦截了OPTIONS请求,需要在过滤器里跳过预检请求的校验:
@Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // 跳过OPTIONS预检请求,直接放行 if (HttpMethod.OPTIONS.name().equalsIgnoreCase(request.getMethod())) { response.setStatus(HttpServletResponse.SC_OK); filterChain.doFilter(request, response); return; } // 原有JWT校验逻辑... }
内容的提问来源于stack exchange,提问作者Rohan
相关产品推荐
相关产品推荐

