后端跨域(Spring Boot 跨域问题终极解决方案(CORS+Nginx))

后端跨域(Spring Boot 跨域问题终极解决方案(CORS+Nginx))
Spring Boot 跨域问题终极解决方案(CORS+Nginx)

在前后端分离架构成为主流的今天,跨域问题早已成为后端开发者绕不开的“拦路虎”。前端页面部署在域名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)。

复杂请求的预检流程:

  1. 前端发起OPTIONS预检请求,携带请求源(Origin)、请求方法(Access-Control-Request-Method)、请求头(Access-Control-Request-Headers);
  2. 后端接收预检请求,返回CORS响应头,告知浏览器允许的源、方法、请求头;
  3. 浏览器校验响应头,若与请求信息匹配,则预检通过,发起业务请求;若不匹配,则拦截请求,提示跨域错误。

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”的终极解决方案,并非简单叠加配置,而是有明确的协同逻辑,避免配置冲突,兼顾效率与安全性:

  1. 开发、测试环境:仅配置Spring Boot CORS,快速适配本地联调,减少Nginx配置的复杂度,提升开发效率;
  2. 生产环境:启用Nginx反向代理,将前端请求转发到后端,同时在Nginx中配置基础CORS响应头,后端CORS配置保留核心限制(如允许的源、Credentials),形成双重保障;
  3. 复杂场景(多源、自定义头):后端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配置需保持一致。

后端跨域(Spring Boot 跨域问题终极解决方案(CORS+Nginx))

总结

跨域问题看似简单,实则涉及浏览器同源策略、CORS预检机制、Nginx反向代理等多个知识点,很多开发者因配置细节遗漏、场景适配不足,导致跨域问题反复出现,影响开发效率和线上稳定性。

💻 配套实训环境与动手练习

本文涉及的相关技术指令、开发环境与工具链已内置在边学边练在线实验室中,无需繁琐安装配置,随时在浏览器中实践体验:

文章版权声明:除非注明,否则均为边学边练网络文章,版权归原作者所有