1. 问题现象与背景分析上周在调试一个前后端分离的Spring Boot项目时遇到了一个诡异的问题前端Vue应用发送的跨域请求始终无法到达后端接口。浏览器控制台显示CORS错误但奇怪的是后端日志完全没有收到任何请求记录。这种情况与常规的跨域问题有所不同——通常跨域问题会先触发预检请求OPTIONS而这次连预检请求都没出现在后端日志中。这个问题困扰了我整整两天时间期间排查了网络配置、Nginx代理、Spring Security过滤器链等多个环节。最终发现是一个容易被忽视的配置项在作祟。本文将完整还原整个排查过程分享其中关键的技术点和排查思路。2. 跨域问题基础排查2.1 常规跨域解决方案验证首先我检查了最基本的跨域配置确保Spring Boot应用已经添加了标准的CORS配置Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOrigins(*) .allowedMethods(GET, POST, PUT, DELETE) .allowCredentials(true) .maxAge(3600); } }同时也在Controller层尝试了注解方式的配置RestController CrossOrigin(origins *) RequestMapping(/api) public class DemoController { // 接口定义 }这两种方式在之前的项目中都被验证过是有效的但这次却都不起作用。这提示问题可能不在常规的CORS配置上。2.2 网络层问题排查接下来我检查了网络层面的配置确认前端请求的URL完全正确没有拼写错误使用Postman直接调用后端接口确认接口本身是可用的检查了Nginx配置确认没有过滤OPTIONS请求location /api { proxy_pass http://backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; if ($request_method OPTIONS) { add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods GET, POST, OPTIONS; add_header Access-Control-Allow-Headers DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range; add_header Access-Control-Max-Age 1728000; add_header Content-Type text/plain; charsetutf-8; add_header Content-Length 0; return 204; } }网络层面一切正常但问题依旧存在。3. 深入排查与问题定位3.1 请求链路追踪为了搞清楚请求到底在哪一步被拦截了我进行了以下操作在浏览器开发者工具中查看网络请求详情在后端应用添加了Filter打印所有进入的请求使用Wireshark抓包分析网络流量发现关键现象浏览器确实发送了OPTIONS预检请求但该请求根本没有到达后端服务器。这说明问题出在请求到达Spring Boot应用之前的环节。3.2 Spring Security配置检查项目中使用Spring Security进行认证授权检查配置发现Configuration EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { Override protected void configure(HttpSecurity http) throws Exception { http.cors().and().csrf().disable() .authorizeRequests() .antMatchers(/api/public/**).permitAll() .anyRequest().authenticated(); } }表面看配置没有问题但实际测试发现OPTIONS请求被Spring Security拦截了。需要特别处理OPTIONS方法.antMatchers(HttpMethod.OPTIONS, /**).permitAll()添加这行配置后OPTIONS请求能够到达后端了但问题仍未完全解决。3.3 隐藏的罪魁祸首Content-Type头进一步分析请求头发现前端发送的请求带有Content-Type: application/json头这触发了CORS的预检机制。但我们的Nginx和后端配置都没有显式允许这个头。解决方案是在CORS配置中添加.allowedHeaders(Content-Type, Authorization, X-Requested-With)同时在Nginx配置中也要对应添加add_header Access-Control-Allow-Headers DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization;4. 完整解决方案与配置4.1 后端完整配置最终的Spring Security配置Configuration EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { Override protected void configure(HttpSecurity http) throws Exception { http.cors().and().csrf().disable() .authorizeRequests() .antMatchers(HttpMethod.OPTIONS, /**).permitAll() .antMatchers(/api/public/**).permitAll() .anyRequest().authenticated(); } Bean CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration new CorsConfiguration(); configuration.setAllowedOrigins(Arrays.asList(*)); configuration.setAllowedMethods(Arrays.asList(GET, POST, PUT, DELETE, OPTIONS)); configuration.setAllowedHeaders(Arrays.asList(Content-Type, Authorization, X-Requested-With)); configuration.setAllowCredentials(true); UrlBasedCorsConfigurationSource source new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration(/**, configuration); return source; } }4.2 前端请求示例前端需要确保正确设置withCredentialsaxios.get(http://api.example.com/data, { withCredentials: true, headers: { Content-Type: application/json } })5. 经验总结与常见陷阱OPTIONS方法必须显式放行Spring Security默认会拦截OPTIONS请求必须显式配置antMatchers(HttpMethod.OPTIONS, /**).permitAll()Content-Type头需要特别处理当Content-Type不是application/x-www-form-urlencoded,multipart/form-data或text/plain时会触发预检请求Nginx配置要完整Nginx层的CORS配置必须与后端保持一致特别是Allowed-Headers部分浏览器缓存问题有时浏览器会缓存CORS策略导致修改配置后仍然报错可以尝试无痕模式或清除缓存withCredentials的影响当使用凭证模式时(allowCredentials(true))不能使用通配符*作为allowedOrigins必须指定具体域名这个问题的排查过程让我深刻认识到跨域问题不仅仅是添加几个CORS头那么简单需要从浏览器行为、网络代理、安全框架等多个层面综合分析。特别是当请求根本没到达后端时要优先排查网络层和Spring Security的配置。