Spring Security 6+Angular 13 OPTIONS请求CORS问题求助
解决方案
核心问题是OPTIONS预检请求未携带JWT,被JwtAuthenticationFilter拦截后无法返回CORS响应头,以下是几种在保证/operator/**接口认证要求的前提下,可落地的修复方案:
方案一:Spring Security中放行OPTIONS请求+全局CORS配置
在SecurityFilterChain中先配置CORS规则,同时明确放行所有OPTIONS请求,让预检请求无需认证即可拿到合法响应头。
@Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http // 优先配置CORS .cors(cors -> cors.configurationSource(corsConfigurationSource())) // 前后端分离场景禁用CSRF .csrf(csrf -> csrf.disable()) // 权限规则配置 .authorizeHttpRequests(auth -> auth // 放行所有OPTIONS预检请求 .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll() // 放行登录接口 .requestMatchers("/auth/login").permitAll() // /operator/**接口仅允许ADMIN角色访问 .requestMatchers("/operator/**").hasRole("ADMIN") // 其余接口需认证 .anyRequest().authenticated() ) // 添加JWT认证过滤器 .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); return http.build(); } // 配置全局CORS规则 @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); // 允许前端域名(生产环境替换为实际域名) config.setAllowedOrigins(List.of("http://localhost:4200")); // 允许的HTTP方法,必须包含OPTIONS config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS")); // 允许所有请求头(包括Authorization) config.setAllowedHeaders(List.of("*")); // 允许携带凭证(如JWT存于Cookie时需开启) config.setAllowCredentials(true); // 预检请求缓存时长,减少重复OPTIONS请求 config.setMaxAge(3600L); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return source; }
方案二:让CORS过滤器优先于JWT过滤器执行
通过FilterRegistrationBean设置CORS过滤器的最高优先级,确保在JWT认证拦截前,OPTIONS请求就能拿到CORS响应头。
@Bean public FilterRegistrationBean<CorsFilter> corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration config = new CorsConfiguration(); config.setAllowedOrigins(List.of("http://localhost:4200")); config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS")); config.setAllowedHeaders(List.of("*")); config.setAllowCredentials(true); config.setMaxAge(3600L); source.registerCorsConfiguration("/**", config); FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>(new CorsFilter(source)); // 设置最高优先级,确保在JWT过滤器前执行 bean.setOrder(Ordered.HIGHEST_PRECEDENCE); return bean; }
同时在Security配置中保持OPTIONS请求的放行规则(同方案一的authorizeHttpRequests配置)。
方案三:在JWT过滤器中直接放行OPTIONS请求
修改自定义的JwtAuthenticationFilter,在认证逻辑前判断请求方法,若为OPTIONS则直接设置CORS头并放行,不执行JWT校验。
@Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // 拦截OPTIONS预检请求,直接返回CORS响应头 if (HttpMethod.OPTIONS.name().equalsIgnoreCase(request.getMethod())) { response.setHeader("Access-Control-Allow-Origin", "http://localhost:4200"); response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"); response.setHeader("Access-Control-Allow-Headers", "*"); response.setHeader("Access-Control-Allow-Credentials", "true"); response.setHeader("Access-Control-Max-Age", "3600"); response.setStatus(HttpServletResponse.SC_OK); return; } // 原有JWT认证逻辑 String jwt = extractJwtFromRequest(request); // ...后续校验、设置认证信息等代码 filterChain.doFilter(request, response); }
注意事项
- 推荐优先使用方案一或方案二,这是Spring生态下的标准配置方式,更易维护;方案三适合快速临时修复。
- 生产环境中
allowedOrigins不要设置为*,需指定具体的前端域名,否则无法携带凭证。
内容的提问来源于stack exchange,提问作者Matteo Vigilante
相关产品推荐
相关产品推荐

