在前后端分离架构成为主流的今天,跨域问题早已成为后端开发者绕不开的“拦路虎”。前端页面部署在域名A,后端接口部署在域名B,当前端发起请求时,浏览器的同源策略会直接拦截请求,导致接口调用失败、数据无法正常交互,严重影响项目开发进度和线上稳定性。
很多开发者在遇到跨域问题时,往往只会简单配置Spring Boot的CORS(跨域资源共享),却忽略了不同场景下的适配问题,导致跨域配置无效、部分请求拦截、线上环境兼容异常等问题。本文将从跨域核心原理入手,深度解析Spring Boot CORS配置的细节的误区,结合Nginx反向代理方案,提供“CORS基础配置+Nginx终极兜底”的全方位解决方案,覆盖开发、测试、生产全场景,帮你彻底解决跨域难题,避坑少走弯路。
跨域问题的核心本质与常见场景
要彻底解决跨域问题,首先要搞懂“跨域”的核心定义、产生原因,以及实际开发中最常见的跨域场景。只有明确本质,才能精准配置、高效避坑,避免盲目照搬配置导致的无效问题。
1.1 跨域的核心定义:什么是同源策略?
跨域的本质是浏览器的“同源策略”限制——浏览器为了保障网页安全,禁止不同“源”的页面之间随意交互数据。这里的“源”由三个要素组成:协议(http/https)、域名(www.xxx.com)、端口(8080/80/443),三者必须完全一致,才属于“同源”,否则即为“跨域”。
举个直观例子,帮助快速判断跨域场景:
- 同源(无跨域):http://www.test.com:8080 和 http://www.test.com:8080/api(协议、域名、端口一致);
- 跨域(协议不同):http://www.test.com 和 https://www.test.com(协议从http变为https);
- 跨域(域名不同):http://www.test.com 和 http://www.test2.com(主域名不同);
- 跨域(子域名不同):http://www.test.com 和 http://api.test.com(主域名相同,子域名不同);
- 跨域(端口不同):http://www.test.com:8080 和 http://www.test.com:80(域名相同,端口不同)。
需要注意的是,同源策略仅针对浏览器端,后端接口之间的调用(如微服务之间的通信)不受同源策略限制,这也是后续Nginx反向代理解决跨域的核心原理之一。
1.2 跨域问题的表现形式与危害
当前端发起跨域请求时,浏览器会先进行“预检请求”(OPTIONS请求),若预检不通过,会直接拦截后续的业务请求,前端控制台会出现典型的跨域错误提示,常见的有三种:
- Access to XMLHttpRequest at 'http://api.test.com' from origin 'http://www.test.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.(最常见,未配置允许的源);
- Access to XMLHttpRequest at 'http://api.test.com' from origin 'http://www.test.com' has been blocked by CORS policy: Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response.(请求头不被允许);
- Access to XMLHttpRequest at 'http://api.test.com' from origin 'http://www.test.com' has been blocked by CORS policy: Method PUT is not allowed by Access-Control-Allow-Methods in preflight response.(请求方法不被允许)。
跨域问题的危害主要体现在两个方面:一是开发阶段,前端无法正常调用后端接口,导致联调受阻,开发效率低下;二是线上阶段,若配置不当,会导致部分用户(不同浏览器、不同环境)无法正常使用功能,甚至出现数据交互异常,影响用户体验和业务正常运转。
1.3 实际开发中最常见的跨域场景
结合前后端分离项目的实际部署情况,以下4种跨域场景最为普遍,也是开发者最常遇到的场景,解决方案需针对性适配:
场景1:本地开发跨域(最常见)
前端项目(Vue/React)运行在本地开发服务器(如Vue的8080端口),后端Spring Boot项目运行在本地另一个端口(如8081端口),前端调用后端接口时,因端口不同导致跨域。这种场景是开发阶段的高频问题,需快速配置解决,不影响联调效率。
场景2:测试环境跨域
测试环境中,前端部署在测试域名(如http://test-front.test.com),后端接口部署在另一个测试域名(如http://test-api.test.com),因域名不同导致跨域。这种场景需兼顾多环境适配,避免测试环境配置与生产环境不一致。
场景3:生产环境跨域(多域名/多端口)
生产环境中,前端可能部署在CDN(如http://cdn.test.com),后端接口部署在应用服务器(如http://api.test.com),甚至存在多前端域名(如PC端、移动端不同域名)调用同一后端接口的情况,跨域配置需支持多源适配,同时保障安全性。
场景4:复杂请求跨域(带自定义头/特殊方法)
当前端发起的请求包含自定义请求头(如Token、Authorization)、特殊请求方法(如PUT、DELETE、PATCH),或请求体为JSON格式时,浏览器会触发“复杂预检请求”,此时仅简单配置允许的源,会导致预检失败,跨域问题无法解决。
1.4 常见跨域解决方案对比(选型关键)
目前解决跨域问题的方案有多种,不同方案的适配场景、复杂度、安全性差异较大,很多开发者因选型不当,导致跨域问题反复出现。以下是4种主流方案的详细对比,帮你快速选型:
解决方案 | 核心原理 | 适配场景 | 优点 | 缺点 |
Spring Boot CORS配置 | 通过后端设置CORS响应头,允许指定源、方法、请求头跨域 | 本地开发、测试环境、简单生产场景 | 配置简单、无需额外依赖、开发效率高 | 复杂场景(多源、特殊请求)适配不足,部分浏览器兼容差 |
Nginx反向代理 | 通过Nginx转发请求,将跨域请求转为同源请求,隐藏后端真实地址 | 生产环境、复杂跨域场景、多源适配 | 兼容性强、安全性高、支持多场景适配,可兼顾负载均衡 | 需额外配置Nginx,对运维人员有一定要求 |
前端代理(如Vue Proxy) | 前端开发环境中,通过代理将跨域请求转发到后端,仅开发阶段生效 | 本地开发阶段 | 配置简单、不影响后端,仅用于开发调试 | 仅适用于开发环境,线上环境无效 |
JSONP | 利用script标签不受同源策略限制,通过回调函数获取数据 | 老旧项目、仅支持GET请求 | 兼容性极强,无需后端复杂配置 | 仅支持GET请求,安全性低,无法携带自定义头 |
结论:最实用、最全面的解决方案是“Spring Boot CORS基础配置 + Nginx反向代理兜底”——开发、测试环境用CORS快速适配,生产环境用Nginx保障兼容性和安全性,兼顾开发效率与线上稳定性,这也是本文重点讲解的终极方案。
CORS核心机制与Nginx反向代理原理
要熟练运用“CORS+Nginx”解决方案,必须深入理解二者的底层原理,明确CORS的预检机制、核心响应头含义,以及Nginx反向代理解决跨域的核心逻辑,避免因配置细节遗漏导致跨域无效。
2.1 CORS核心机制:预检请求与响应头解析
CORS(Cross-Origin Resource Sharing,跨域资源共享)是W3C标准,本质是通过后端设置响应头,告知浏览器“允许哪个源、哪种方法、哪些请求头”的跨域请求,浏览器根据响应头判断是否允许跨域交互。CORS的核心流程分为“简单请求”和“复杂请求”,二者的处理逻辑不同。
2.1.1 简单请求(无需预检)
满足以下条件的请求,属于简单请求,浏览器会直接发起业务请求,无需提前发送预检请求(OPTIONS):
- 请求方法为GET、POST、HEAD三者之一;
- 请求头仅包含浏览器默认头(如Accept、Accept-Language、Content-Language),不包含自定义头;
- 请求体类型为application/x-www-form-urlencoded、multipart/form-data、text/plain三者之一(不包含JSON格式)。
简单请求的跨域控制,仅需后端返回核心响应头,浏览器即可允许交互。
2.1.2 复杂请求(需预检)
不满足简单请求条件的,均为复杂请求,浏览器会先发送一次OPTIONS预检请求,询问后端“是否允许当前跨域请求”,只有预检通过后,才会发送真正的业务请求。常见的复杂请求场景:
- 请求方法为PUT、DELETE、PATCH、OPTIONS等;
- 请求头包含自定义头(如Token、Authorization、Content-Type: application/json);
- 请求体为JSON格式(Content-Type: application/json)。
复杂请求的预检流程:
- 前端发起OPTIONS预检请求,携带请求源(Origin)、请求方法(Access-Control-Request-Method)、请求头(Access-Control-Request-Headers);
- 后端接收预检请求,返回CORS响应头,告知浏览器允许的源、方法、请求头;
- 浏览器校验响应头,若与请求信息匹配,则预检通过,发起业务请求;若不匹配,则拦截请求,提示跨域错误。
2.1.3 CORS核心响应头(必懂,配置关键)
CORS的配置本质是设置后端响应头,以下6个核心响应头决定了跨域是否生效,必须精准理解每个头的含义,避免配置错误:
- Access-Control-Allow-Origin:最核心的响应头,指定允许跨域的源。可设置具体域名(如http://www.test.com)、多个域名(用逗号分隔)、通配符*(允许所有源,不推荐生产环境,存在安全风险);
- Access-Control-Allow-Methods:允许跨域的请求方法,如GET,POST,PUT,DELETE,OPTIONS,多个方法用逗号分隔;
- Access-Control-Allow-Headers:允许跨域的请求头,如Content-Type,Token,Authorization,需包含前端所有自定义头,否则预检失败;
- Access-Control-Allow-Credentials:是否允许跨域请求携带Cookie、Session信息,值为true/false。若前端需携带Cookie(如登录态),需设置为true,且Access-Control-Allow-Origin不能为*;
- Access-Control-Max-Age:预检请求的缓存时间(单位:秒),设置后,浏览器在缓存时间内无需重复发送预检请求,减少请求次数,提升性能;
- Access-Control-Expose-Headers:允许前端获取的后端响应头,默认情况下,前端只能获取Cache-Control、Content-Language等少数头,若需获取自定义响应头(如Token),需在此配置。
2.2 Nginx反向代理解决跨域的核心原理
Nginx反向代理解决跨域的核心逻辑是“消除跨域差异”——通过Nginx作为中间层,将前端的跨域请求转发到后端接口,前端请求的是Nginx的地址(与前端同源),Nginx再将请求转发到后端真实地址,后端响应结果经Nginx转发回前端,从而绕过浏览器的同源策略限制。
简单来说,就是“前端→Nginx(同源)→后端(跨域)”,浏览器只看到前端与Nginx的同源请求,无法感知后端的跨域差异,从而避免跨域拦截。其核心优势在于:
- 兼容性极强:不受浏览器版本、请求类型限制,无论是简单请求还是复杂请求,均可完美适配;
- 安全性高:隐藏后端真实接口地址,避免直接暴露后端服务,同时可配置权限控制、限流等功能;
- 多场景适配:支持多前端域名、多后端接口的跨域配置,可同时兼顾负载均衡、静态资源缓存等需求;
- 无需修改后端代码:对于已上线的项目,若跨域配置不当,可通过Nginx快速兜底,无需修改后端CORS配置。
Nginx解决跨域的核心配置逻辑:一是配置反向代理,将前端请求转发到后端;二是设置CORS响应头,辅助浏览器校验(可选,若后端已配置CORS,Nginx可无需重复配置,避免冲突)。
2.3 CORS与Nginx协同工作的最佳逻辑
“CORS+Nginx”的终极解决方案,并非简单叠加配置,而是有明确的协同逻辑,避免配置冲突,兼顾效率与安全性:
- 开发、测试环境:仅配置Spring Boot CORS,快速适配本地联调,减少Nginx配置的复杂度,提升开发效率;
- 生产环境:启用Nginx反向代理,将前端请求转发到后端,同时在Nginx中配置基础CORS响应头,后端CORS配置保留核心限制(如允许的源、Credentials),形成双重保障;
- 复杂场景(多源、自定义头):后端CORS配置允许的源为Nginx地址,Nginx配置允许所有前端源(或指定多源),通过Nginx统一转发,避免后端配置过于繁琐。
具体实战:CORS+Nginx跨域解决方案完整落地
结合实际开发场景,本文以“前端Vue项目(http://www.test.com)+ Spring Boot后端(http://api.test.com:8081)+ Nginx反向代理”为例,分三个阶段实现跨域解决方案落地:开发环境(仅CORS)、测试环境(CORS优化)、生产环境(CORS+Nginx),提供完整代码、配置及测试验证步骤,确保可直接复制使用。
实战环境说明:Spring Boot 4.0.0、JDK 17、Nginx 1.24.0、Vue 3(前端),覆盖简单请求、复杂请求、携带Cookie等常见场景。
3.1 开发环境:Spring Boot CORS基础配置(快速适配联调)
开发环境中,前端运行在本地8080端口(http://localhost:8080),后端运行在8081端口(http://localhost:8081),仅需配置Spring Boot CORS,即可解决跨域问题,无需启动Nginx。
3.1.1 方式1:全局CORS配置(推荐,适配所有接口)
创建CORS配置类,全局统一配置跨域规则,适用于所有接口,无需在每个接口单独配置,代码简洁、便于维护:
package com.example.springbootcors.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.cors.CorsConfiguration;import org.springframework.web.cors.UrlBasedCorsConfigurationSource;import org.springframework.web.filter.CorsFilter;/** * 全局CORS跨域配置类(开发环境) */@Configurationpublic class GlobalCorsConfig { @Bean public CorsFilter corsFilter() { // 1. 创建CORS配置对象 CorsConfiguration corsConfig = new CorsConfiguration(); // 允许的跨域源(开发环境可设置为*,方便联调;生产环境需指定具体域名) corsConfig.addAllowedOrigin("http://localhost:8080"); // 允许携带Cookie(若前端需登录态,必须设置为true) corsConfig.setAllowCredentials(true); // 允许的请求方法(覆盖所有常见方法) corsConfig.addAllowedMethod("*"); // 允许的请求头(覆盖所有自定义头,避免预检失败) corsConfig.addAllowedHeader("*"); // 允许前端获取的响应头(若有自定义响应头,需在此添加) corsConfig.addExposedHeader("Token"); // 预检请求缓存时间(3600秒,1小时,减少预检请求次数) corsConfig.setMaxAge(3600L); // 2. 配置CORS源映射(指定哪些接口生效,/**表示所有接口) UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", corsConfig); // 3. 返回CORS过滤器 return new CorsFilter(source); }}代码说明:
- addAllowedOrigin("http://localhost:8080"):仅允许本地前端8080端口跨域,开发环境可改为addAllowedOrigin("*"),简化配置;
- setAllowCredentials(true):若前端需携带Cookie(如登录后保持会话),必须设置为true,否则前端无法获取后端返回的Cookie;
- addAllowedMethod("*")、addAllowedHeader("*"):开发环境可直接设置为*,覆盖所有方法和请求头,避免遗漏导致预检失败;
- setMaxAge(3600L):预检请求缓存1小时,减少浏览器发送的预检请求数量,提升开发阶段的接口响应速度。
3.1.2 方式2:局部CORS配置(针对单个接口/控制器)
若部分接口需要特殊的跨域配置(如仅允许特定源),可使用@CrossOrigin注解,作用于单个控制器或单个接口,优先级高于全局配置:
package com.example.springbootcors.controller;import org.springframework.web.bind.annotation.*;import java.util.HashMap;import java.util.Map;/** * 测试接口控制器(局部CORS配置) */@RestController@RequestMapping("/api/test")// 控制器级别的跨域配置,作用于该控制器下所有接口@CrossOrigin( origins = "http://localhost:8080", // 允许的源 allowCredentials = true, // 允许携带Cookie allowedHeaders = "*", // 允许的请求头 methods = {RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT}, // 允许的方法 maxAge = 3600)public class TestController { // 单个接口的跨域配置,优先级高于控制器配置 @CrossOrigin(origins = "http://localhost:8080", allowCredentials = true) @GetMapping("/get") public Map getTest() { Map result = new HashMap<>(); result.put("code", 200); result.put("message", "get请求跨域成功"); return result; } // 复杂请求(POST+JSON) @PostMapping("/post/json") public Map postJson(@RequestBody Map params) { Map result = new HashMap<>(); result.put("code", 200); result.put("message", "post+JSON请求跨域成功"); result.put("params", params); return result; } // 带自定义头的请求(Token) @GetMapping("/token") public Map testToken(@RequestHeader("Token") String token) { Map result = new HashMap<>(); result.put("code", 200); result.put("message", "带Token请求跨域成功"); result.put("token", token); return result; }} 3.1.3 开发环境测试验证
1. 启动Spring Boot项目,接口地址:http://localhost:8081/api/test;
2. 前端Vue项目发起请求(以Axios为例),测试不同类型的请求:
// 1. 简单GET请求axios.get("http://localhost:8081/api/test/get") .then(res => console.log(res.data)) .catch(err => console.log(err));// 2. 复杂请求(POST+JSON)axios.post("http://localhost:8081/api/test/post/json", {name: "test"}) .then(res => console.log(res.data)) .catch(err => console.log(err));// 3. 带自定义头(Token)的请求axios.get("http://localhost:8081/api/test/token", { headers: { Token: "123456789" }}) .then(res => console.log(res.data)) .catch(err => console.log(err));3. 预期结果:前端控制台无跨域错误,正常获取后端返回的响应数据,三种请求均能成功执行。
3.2 测试环境:Spring Boot CORS优化配置(多源适配)
测试环境中,前端部署在测试域名(http://test-front.test.com),后端部署在测试域名(http://test-api.test.com:8081),且可能存在多个前端域名(如测试环境、预发布环境)调用后端接口,此时需优化CORS配置,支持多源适配,同时关闭开发环境的宽松配置,贴近生产环境。
3.2.1 测试环境CORS优化配置
修改全局CORS配置,支持多个指定源,关闭通配符*,增加安全性,同时适配复杂请求:
package com.example.springbootcors.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.cors.CorsConfiguration;import org.springframework.web.cors.UrlBasedCorsConfigurationSource;import org.springframework.web.filter.CorsFilter;import java.util.Arrays;/** * 全局CORS跨域配置类(测试环境) */@Configurationpublic class GlobalCorsConfig { @Bean public CorsFilter corsFilter() { CorsConfiguration corsConfig = new CorsConfiguration(); // 支持多个前端域名(测试环境、预发布环境),避免使用* corsConfig.setAllowedOrigins(Arrays.asList( "http://test-front.test.com", "http://pre-front.test.com" )); // 允许携带Cookie corsConfig.setAllowCredentials(true); // 允许的请求方法(明确指定,不使用*,提升安全性) corsConfig.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); // 允许的请求头(明确指定常见头和自定义头) corsConfig.setAllowedHeaders(Arrays.asList( "Content-Type", "Token", "Authorization", "Accept", "Origin" )); // 允许前端获取的自定义响应头 corsConfig.setExposedHeaders(Arrays.asList("Token", "Refresh-Token")); // 预检请求缓存时间(3600秒) corsConfig.setMaxAge(3600L); // 配置接口映射,/**表示所有接口,也可指定特定接口前缀(如/api/**) UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/api/**", corsConfig); return new CorsFilter(source); }}优化点说明:
- setAllowedOrigins:替换通配符*,明确指定允许的前端域名,避免非法域名跨域调用,提升安全性;
- setAllowedMethods、setAllowedHeaders:明确指定允许的方法和请求头,不使用*,减少安全风险;
- setExposedHeaders:添加自定义响应头(如Refresh-Token),确保前端能正常获取后端返回的自定义头。
3.2.2 测试环境特殊场景适配
场景1:前端携带Cookie登录
若前端需要通过Cookie保持登录态,除了在CORS配置中设置setAllowCredentials(true),还需注意两点:
- 后端接口需设置Cookie的SameSite属性为None,否则浏览器会拦截Cookie(尤其Chrome浏览器);
- 前端Axios需设置withCredentials: true,允许携带Cookie。
后端设置Cookie示例:
@GetMapping("/login")public Map login(HttpServletResponse response) { // 设置Cookie,SameSite=None,允许跨域携带 Cookie cookie = new Cookie("SESSIONID", "1234567890abcdef"); cookie.setPath("/"); cookie.setDomain("test-api.test.com"); // 后端域名 cookie.setSameSite("None"); // 关键:允许跨域携带Cookie cookie.setSecure(true); // 若使用HTTPS,需设置为true cookie.setMaxAge(3600); // 有效期1小时 response.addCookie(cookie); Map result = new HashMap<>(); result.put("code", 200); result.put("message", "登录成功,Cookie已设置"); return result;} 前端Axios配置示例:
// 全局配置,允许携带Cookieaxios.defaults.withCredentials = true;// 登录请求axios.get("http://test-api.test.com:8081/api/test/login") .then(res => console.log(res.data)) .catch(err => console.log(err));场景2:OPTIONS预检请求被拦截
测试环境中,若后端开启了拦截器(如登录拦截、权限拦截),可能会拦截OPTIONS预检请求,导致跨域失败。解决方案:在拦截器中放行OPTIONS请求。
package com.example.springbootcors.interceptor;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;import org.springframework.stereotype.Component;import org.springframework.web.servlet.HandlerInterceptor;/** * 登录拦截器(放行OPTIONS预检请求) */@Componentpublic class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 放行OPTIONS预检请求,避免跨域预检失败 if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { return true; } // 其他拦截逻辑(如登录校验、权限校验) String token = request.getHeader("Token"); if (token == null || token.isEmpty()) { response.setStatus(401); response.getWriter().write("未登录,请先登录"); return false; } // 校验通过,放行 return true; }}同时,在Spring Boot配置类中注册拦截器,确保拦截器生效:
package com.example.springbootcors.config;import com.example.springbootcors.interceptor.LoginInterceptor;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configurationpublic class WebMvcConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { // 注册登录拦截器,排除不需要拦截的接口 registry.addInterceptor(new LoginInterceptor()) .addPathPatterns("/api/**") // 拦截所有/api开头的接口 .excludePathPatterns("/api/test/login"); // 放行登录接口 }}3.3 生产环境:CORS+Nginx终极解决方案(兼容所有场景)
生产环境中,前端部署在CDN或静态服务器(http://www.test.com),后端部署在应用服务器(http://api.test.com:8081),此时仅靠CORS配置可能存在浏览器兼容问题(如老旧浏览器不支持CORS)、多源适配繁琐等问题,结合Nginx反向代理,实现终极跨域解决方案。
生产环境架构:前端(http://www.test.com)→ Nginx(http://www.test.com/api)→ 后端(http://api.test.com:8081/api),Nginx将前端的/api请求转发到后端,同时配置CORS响应头,形成双重保障。
3.3.1 步骤1:Spring Boot CORS生产环境配置(精简配置)
生产环境中,后端CORS配置仅保留核心限制,具体允许的源由Nginx控制,避免重复配置导致冲突:
package com.example.springbootcors.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.cors.CorsConfiguration;import org.springframework.web.cors.UrlBasedCorsConfigurationSource;import org.springframework.web.filter.CorsFilter;/** * 全局CORS跨域配置类(生产环境) */@Configurationpublic class GlobalCorsConfig { @Bean public CorsFilter corsFilter() { CorsConfiguration corsConfig = new CorsConfiguration(); // 允许的源为Nginx地址(避免直接暴露前端域名,由Nginx统一转发) corsConfig.addAllowedOrigin("http://www.test.com"); // 允许携带Cookie corsConfig.setAllowCredentials(true); // 允许的请求方法 corsConfig.addAllowedMethod("*"); // 允许的请求头 corsConfig.addAllowedHeader("*"); // 允许前端获取的自定义响应头 corsConfig.addExposedHeader("Token", "Refresh-Token"); // 预检请求缓存时间(86400秒,24小时) corsConfig.setMaxAge(86400L); // 仅对/api开头的接口生效 UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/api/**", corsConfig); return new CorsFilter(source); }}3.3.2 步骤2:Nginx配置(反向代理+跨域兜底)
修改Nginx配置文件(nginx.conf),主要配置两个核心功能:反向代理(转发请求到后端)、CORS响应头(兜底跨域配置),确保所有跨域场景兼容。
# Nginx配置文件(nginx.conf)worker_processes 1; # 工作进程数,根据服务器CPU核心数调整events { worker_connections 1024; # 每个工作进程的最大连接数}http { include mime.types; default_type application/octet-stream; # 日志配置(生产环境建议开启,便于排查跨域问题) log_format main '$remote_addr [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log logs/access.log main; sendfile on; keepalive_timeout 65; # 连接超时时间 # 前端静态资源配置(若前端部署在Nginx,需配置) server { listen 80; server_name www.test.com; # 前端域名 # 前端静态资源目录(Vue/React打包后的dist目录) location / { root /usr/local/nginx/html/dist; # 静态资源路径 index index.html index.htm; try_files $uri $uri/ /index.html; # 解决前端路由刷新404问题 } # 反向代理配置:将/api开头的请求转发到后端 location /api { # 后端接口真实地址 proxy_pass http://api.test.com:8081/api; # 传递真实客户端IP proxy_set_header X-Real-IP $remote_addr; # 传递客户端请求头Host proxy_set_header Host $host; # 传递X-Forwarded-For头,便于后端获取客户端IP proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 传递X-Forwarded-Proto头,告知后端请求协议(http/https) proxy_set_header X-Forwarded-Proto $scheme; # CORS跨域兜底配置(核心,适配所有浏览器) # 允许的跨域源(生产环境可指定具体前端域名,或*,根据需求调整) add_header Access-Control-Allow-Origin http://www.test.com; # 允许携带Cookie add_header Access-Control-Allow-Credentials true; # 允许的请求方法 add_header Access-Control-Allow-Methods "GET,POST,PUT,DELETE,OPTIONS"; # 允许的请求头 add_header Access-Control-Allow-Headers "Content-Type,Token,Authorization,Accept,Origin"; # 允许前端获取的自定义响应头 add_header Access-Control-Expose-Headers "Token,Refresh-Token"; # 预检请求缓存时间(24小时) add_header Access-Control-Max-Age 86400; # 放行OPTIONS预检请求,直接返回200,无需转发到后端 if ($request_method = 'OPTIONS') { return 200; } } }}Nginx配置关键说明:
- proxy_pass http://api.test.com:8081/api:将前端发起的http://www.test.com/api请求,转发到后端http://api.test.com:8081/api,实现同源请求;
- add_header配置CORS响应头:与后端CORS配置一致,形成双重保障,即使后端CORS配置异常,Nginx也能兜底解决跨域;
- if ($request_method = 'OPTIONS') { return 200; }:直接放行OPTIONS预检请求,无需转发到后端,减少后端压力,同时避免预检请求被后端拦截;
- 前端静态资源配置:若前端打包后部署在Nginx,需配置location /,指定静态资源目录,同时解决前端路由刷新404问题(try_files $uri $uri/ /index.html)。
3.3.3 步骤3:HTTPS环境适配(生产环境必配)
生产环境通常使用HTTPS协议,此时跨域配置需额外注意两点:一是Cookie需设置Secure=true(仅允许HTTPS环境携带);二是Nginx需配置SSL证书,同时CORS响应头的Access-Control-Allow-Origin需使用HTTPS域名。
Nginx HTTPS配置示例(修改server块):
# HTTPS服务器配置server { listen 443 ssl; # 监听443端口(HTTPS默认端口) server_name www.test.com; # 前端域名 # SSL证书配置(替换为自己的证书路径) ssl_certificate /usr/local/nginx/ssl/www.test.com.crt; ssl_certificate_key /usr/local/nginx/ssl/www.test.com.key; # SSL优化配置 ssl_session_cache shared:SSL:1m; ssl_session_timeout 5m; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; # 前端静态资源配置 location / { root /usr/local/nginx/html/dist; index index.html index.htm; try_files $uri $uri/ /index.html; } # 反向代理+跨域配置(HTTPS版本) location /api { proxy_pass https://api.test.com:8443/api; # 后端HTTPS地址 proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # CORS配置(使用HTTPS域名) add_header Access-Control-Allow-Origin https://www.test.com; add_header Access-Control-Allow-Credentials true; add_header Access-Control-Allow-Methods "GET,POST,PUT,DELETE,OPTIONS"; add_header Access-Control-Allow-Headers "Content-Type,Token,Authorization,Accept,Origin"; add_header Access-Control-Expose-Headers "Token,Refresh-Token"; add_header Access-Control-Max-Age 86400; if ($request_method = 'OPTIONS') { return 200; } }}# HTTP自动跳转HTTPS(可选,推荐)server { listen 80; server_name www.test.com; return 301 https://$host$request_uri; # 将HTTP请求自动跳转至HTTPS}3.3.4 生产环境测试验证
1. 部署前端项目:将Vue/React打包后的dist目录,上传到Nginx的/usr/local/nginx/html/dist目录;
2. 启动Nginx:执行nginx -s start(或nginx -s reload,若已启动);
3. 启动Spring Boot后端项目,确保后端接口可正常访问(https://api.test.com:8443/api);
4. 前端访问https://www.test.com,发起各类请求(简单请求、复杂请求、带Cookie请求),验证跨域是否生效;
5. 预期结果:所有请求均能正常响应,前端控制台无跨域错误,Cookie能正常携带,不同浏览器(Chrome、Firefox、Edge)均能兼容。
3.4 常见跨域坑点及解决方案(实战避坑关键)
在实际配置过程中,很多开发者会遇到“配置了CORS但跨域仍无效”“部分请求成功、部分失败”等问题,以下是8个高频坑点,结合实战经验给出解决方案,帮你快速避坑。
坑点1:Access-Control-Allow-Origin设为*,但仍无法跨域
原因分析:当Access-Control-Allow-Credentials设置为true时,Access-Control-Allow-Origin不能为*,浏览器会直接拦截请求,这是最常见的配置错误。
解决方案:将Access-Control-Allow-Origin改为具体的域名(如http://www.test.com),或关闭AllowCredentials(若不需要携带Cookie)。
坑点2:OPTIONS预检请求被后端拦截,返回403/401
原因分析:后端拦截器(如登录拦截、权限拦截)未放行OPTIONS请求,导致预检失败,后续业务请求无法发起。
解决方案:在所有拦截器中,优先放行OPTIONS请求,参考3.2.2节的拦截器配置。
坑点3:前端携带Cookie,但后端无法获取
原因分析:1. 后端CORS未设置AllowCredentials=true;2. Cookie未设置SameSite=None;3. 前端Axios未设置withCredentials=true;4. 域名不一致(Cookie的Domain属性与前端域名不匹配)。
解决方案:逐一检查上述4个配置,确保均正确设置,参考3.2.2节的Cookie配置。
坑点4:复杂请求(POST+JSON)预检失败,提示“Request header field content-type is not allowed”
原因分析:后端CORS配置的AllowedHeaders中,未包含Content-Type请求头,或未设置为*,导致预检失败。
解决方案:在AllowedHeaders中添加Content-Type,或直接设置为*(开发环境)。
坑点5:Nginx配置了反向代理,但跨域仍无效
原因分析:1. Nginx的add_header配置未生效(可能是配置位置错误,需放在location /api块中);2. 后端CORS配置与Nginx配置冲突(如AllowedOrigin不一致);3. 前端请求地址未使用Nginx的代理地址(仍直接请求后端地址)。
解决方案:1. 检查Nginx配置位置,确保add_header在location /api块中;2. 统一后端与Nginx的CORS配置;3. 前端请求地址改为Nginx的代理地址(如http://www.test.com/api)。
坑点6:HTTPS环境下,跨域请求提示“Mixed Content”
原因分析:前端使用HTTPS协议,但请求的后端地址是HTTP协议,浏览器禁止混合协议请求,导致跨域失败。
解决方案:将后端接口改为HTTPS协议,同时修改Nginx的proxy_pass为HTTPS地址,参考3.3.3节的HTTPS配置。
坑点7:多前端域名跨域,配置后部分域名无法访问
原因分析:后端CORS的AllowedOrigins未添加所有前端域名,或使用了通配符*但开启了AllowCredentials=true。
解决方案:1. 在AllowedOrigins中添加所有需要跨域的前端域名(用逗号分隔);2. 若域名较多,可通过Nginx统一配置,后端允许Nginx地址跨域。
坑点8:前端能获取响应数据,但无法获取自定义响应头
原因分析:后端CORS配置的ExposedHeaders中,未添加自定义响应头(如Token、Refresh-Token),导致前端无法获取。
解决方案:在ExposedHeaders中添加需要前端获取的自定义响应头,后端和Nginx配置需保持一致。

总结
跨域问题看似简单,实则涉及浏览器同源策略、CORS预检机制、Nginx反向代理等多个知识点,很多开发者因配置细节遗漏、场景适配不足,导致跨域问题反复出现,影响开发效率和线上稳定性。
💻 配套实训环境与动手练习
本文涉及的相关技术指令、开发环境与工具链已内置在边学边练在线实验室中,无需繁琐安装配置,随时在浏览器中实践体验: