Spring非Boot应用中CORS过滤器的顺序设置问题
我懂你这种头疼——在非Spring Boot的Spring项目里搭Spring Security OAuth2做REST接口安全,还得处理CORS,分开给OAuth端点加过滤器、其他用WebMvcConfigurer的方式虽然能跑,但偶尔抽风出异常确实闹心。下面给你几个靠谱的优化方案,帮你彻底解决这个问题:
1. 统一CORS配置,告别混合方案
分开配置OAuth端点和普通端点的CORS,很容易因为过滤器执行顺序、配置规则冲突导致不稳定。最稳妥的方式是通过Spring Security全局处理所有请求的CORS,包括OAuth的/oauth/token、/oauth/authorize等端点:
- 先把
AbstractAnnotationConfigDispatcherServletInitializer里加的CORS过滤器,还有WebMvcConfigurer中的CORS配置全部移除 - 在你的Spring Security配置类(不管是继承
WebSecurityConfigurerAdapter还是用SecurityFilterChain的新方式)里添加统一的CORS配置:
@Override protected void configure(HttpSecurity http) throws Exception { http // 先启用CORS配置 .cors(cors -> cors.configurationSource(corsConfigurationSource())) // 你的其他安全规则配置... .authorizeRequests(auth -> auth .antMatchers("/oauth/**").permitAll() // 根据你的业务需求调整权限 .anyRequest().authenticated() ) // 这里替换成你实际的OAuth2资源服务器配置,比如JWT或令牌存储 .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults())); } private CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); // 替换成你的前端域名,生产环境千万别用*,会有安全风险 config.setAllowedOrigins(Arrays.asList("https://your-frontend-app.com")); config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); config.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type")); // 如果前端需要带Cookie或认证信息,一定要开这个 config.setAllowCredentials(true); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); // 对所有端点应用这个CORS规则 source.registerCorsConfiguration("/**", config); return source; }
这种方式能保证所有请求都经过一致的CORS处理,从根源上避免过滤器顺序冲突的问题。
2. 强制CORS过滤器优先执行(如果坚持混合配置)
要是你不想改动现有架构,必须保留混合配置,那一定要确保CORS过滤器的执行顺序早于Spring Security的所有过滤器,不然OPTIONS预检请求会被Spring Security先拦截,导致CORS验证失败:
- 在
AbstractAnnotationConfigDispatcherServletInitializer的onStartup方法里注册过滤器时,指定最高优先级:
@Override public void onStartup(ServletContext servletContext) throws ServletException { super.onStartup(servletContext); FilterRegistration.Dynamic corsFilter = servletContext.addFilter("oauthCorsFilter", new CorsFilter(corsConfigSource())); corsFilter.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), true, "/oauth/**"); // 设置最高优先级,确保比Spring Security的过滤器先执行 corsFilter.setOrder(Ordered.HIGHEST_PRECEDENCE); }
同时要注意,WebMvcConfigurer里的CORS配置和过滤器的规则要保持一致,比如不要一边开allowCredentials一边关闭,避免出现矛盾的配置。
3. 放行OPTIONS预检请求
OAuth2的端点在处理OPTIONS请求时,很容易被Spring Security的CSRF保护拦截,导致CORS异常。可以在Spring Security配置里专门放行OPTIONS请求:
@Override protected void configure(HttpSecurity http) throws Exception { http // 忽略OAuth端点的CSRF校验,避免OPTIONS请求被拦截 .csrf(csrf -> csrf.ignoringAntMatchers("/oauth/**")) .authorizeRequests(auth -> auth // 放行所有OPTIONS预检请求 .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() .antMatchers("/oauth/**").permitAll() .anyRequest().authenticated() ); }
这样OPTIONS请求就能顺利通过CORS验证,不会被安全规则卡掉。
4. 调试过滤器执行流程
如果还是偶尔出异常,建议加日志跟踪过滤器的执行情况:
- 在你的CORS过滤器里添加日志,记录请求的URL、HTTP方法、请求头,以及过滤器是否被触发
- 查看Spring Security的过滤器链顺序,确认CORS过滤器确实在
SecurityContextPersistenceFilter之前执行
总的来说,用Spring Security统一配置CORS是最省心的方案,能避免混合配置带来的各种坑,保证所有端点的CORS处理稳定一致。
内容的提问来源于stack exchange,提问作者Nima Ajdari

