【同源策略】跨域问题解决方法(多种)
一. 跨域问题概述
1. 什么是跨域
跨域问题是由于浏览器的同源策略(Same-Origin Policy)导致的安全限制。当一个请求的协议、域名、端口三者中有任何一个与当前页面不一致时,就会触发跨域限制。
2. 跨域的表现形式
http://localhost:8080 → http://localhost:8888 → 端口不同 → 跨域
http://www.example.com → https://www.example.com → 协议不同 → 跨域
http://www.example.com → http://api.example.com → 域名不同 → 跨域
http://192.168.1.1 → http://www.example.com → IP与域名 → 跨域
3. 跨域错误示例
二、 前端解决方案
1. Vue CLI 代理配置
文件位置:项目根目录 /vue.config.js
module.exports = {devServer: {proxy: {// 匹配所有以 '/api' 开头的请求路径'/api': {target: 'http://localhost:8080', // 后端接口地址changeOrigin: true, // 允许跨域请求pathRewrite: {'^/api': '' // 将请求路径中的 '/api' 去掉},// 处理HTTPS请求secure: false,// 超时时间timeout: 5000}}}
}
使用方式:
- 直接在前端代码中使用
/api/xxx
调用后端接口 - 修改配置后需要重启前端服务
- 只在开发环境生效
2. Vite 代理配置
文件位置:项目根目录 /vite.config.js
export default {server: {proxy: {'/api': {target: 'http://localhost:8080',changeOrigin: true,rewrite: (path) => path.replace(/^\/api/, ''),// 处理WebSocketws: true}}}
}
使用方式:
- 在前端代码中使用
/api/xxx
调用后端接口 - 只在开发环境生效
- 支持热更新,修改后无需重启
三、后端解决方案
1. @CrossOrigin 注解(局部跨域)
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/api")
// 允许来自 http://localhost:5173 的跨域请求
@CrossOrigin(origins = "http://localhost:5173", allowCredentials = "true", // 允许携带cookiemaxAge = 3600) // 预检请求缓存时间
public class ApiController {// 接口方法
}
使用方式:
- 直接在 Controller 类或方法上添加注解
- 可以精确控制允许的域名
- 适合开发环境和简单部署
2. 全局 CORS 配置
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class CorsConfig implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**") // 匹配所有请求路径.allowedOriginPatterns("*") // 允许所有域名(生产环境建议指定具体域名).allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 允许的请求方法.allowedHeaders("*") // 允许的请求头.allowCredentials(true) // 允许携带cookie.maxAge(3600); // 预检请求缓存时间}
}
使用方式:
- 创建配置类并实现 WebMvcConfigurer 接口
- 全局生效,无需在每个 Controller 添加注解
- 生产环境建议将 allowedOriginPatterns 改为具体域名
3. Spring Security 中的 CORS 配置
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;@Configuration
@EnableWebSecurity
public class SecurityConfig {@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.cors().and() // 启用CORS.csrf().disable(); // 禁用CSRF(根据需要决定)// 其他安全配置return http.build();}@Beanpublic CorsConfigurationSource corsConfigurationSource() {CorsConfiguration configuration = new CorsConfiguration();configuration.addAllowedOriginPattern("*");configuration.addAllowedMethod("*");configuration.addAllowedHeader("*");configuration.setAllowCredentials(true);UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration("/**", configuration);return source;}
}
使用方式:
- 在 Spring Security 配置中添加 cors () 配置
- 需要单独配置 CorsConfigurationSource
- 注意与其他安全配置的兼容性
原因: Spring Security 的过滤器链优先级高于 Spring MVC 的 CORS 处理机制,会先拦截请求并进行安全校验,可能导致原本的跨域配置失效。
过滤器链执行顺序问题
Spring Security 通过一系列过滤器(如UsernameAuthenticationFilter
、CsrfFilter
等)处理请求,这些过滤器的执行顺序在 Spring MVC 的 CORS 过滤器之前。如果请求被 Spring Security 拦截且未通过校验(包括跨域相关的头信息校验),会直接被拒绝,根本不会到达 Spring MVC 的 CORS 处理逻辑。默认禁用跨域支持
Spring Security 默认不开启跨域支持,即使你配置了WebMvcConfigurer
的addCorsMappings
或@CrossOrigin
,也会被 Security 的过滤器拦截。例如,预检请求(OPTIONS 方法)可能被 Security 直接拒绝,因为默认配置中 OPTIONS 请求未被放行。需要显式启用 CORS 支持
必须在 Spring Security 配置中显式声明启用 CORS,并指定 CORS 配置源,才能让跨域规则生效。
四、生产环境解决方案
注意:
前端代理(如 Vue CLI/Vite 的 devServer.proxy
)不能用于生产环境(因为它是开发环境的临时代理,打包后会失效)打包后脚手架的代理逻辑会被移除,前端直接运行在静态服务器(如 Nginx、Apache)上,此时无法再通过前端代理解决跨域,生产环境的前端是 “纯静态资源”,没有代理能力。
后端跨域配置(如 SpringBoot 的全局 CORS、@CrossOrigin)可以用于生产环境。
--> Nginx 反向代理配置
# Nginx 配置文件
server {listen 80;server_name yourdomain.com; # 前端和后端共用的域名# 处理前端静态资源(与前端同域)location / {root /usr/share/nginx/html; # 前端打包后的dist目录index index.html;try_files $uri $uri/ /index.html; # 支持 Vue Router history模式}# 反向代理后端接口(关键配置)location /api/ {# 转发到后端服务器proxy_pass http://backend-server:8080/; # 后端实际地址# 传递必要的请求头proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_set_header X-Forwarded-Proto $scheme;}
}
使用方式:
- 在 Nginx 配置文件中添加上述配置
- 将前端打包文件放到指定目录
- 重启 Nginx 服务
- 前端直接使用相对路径调用接口
浏览器的跨域限制是针对「不同源的客户端请求」,而 Nginx 反向代理会让前端请求先发送到 Nginx 服务器(与前端同域),再由 Nginx 转发到后端服务器。此时,前端与 Nginx 同域,Nginx 与后端属于服务器间通信(无浏览器跨域限制),从而绕过跨域问题。
Nginx 解决跨域的本质是「将跨域请求转为同域请求」:前端请求先发送到 Nginx(与前端同域,如 https://yourdomain.com
),再由 Nginx 转发到后端(如 http://192.168.1.100:8080
)。此时浏览器认为是 “同域请求”,不会触发跨域限制,同时 Nginx 作为服务器间通信,无需遵守浏览器同源策略。
五、推荐使用方式
开发环境
前端代理(Vue CLI/Vite) + @CrossOrigin注解
生产环境
生产环境中,最佳实践是「Nginx 为主,后端 CORS 全局配置 为辅」,而非二选一:
- 主要依赖 Nginx 反向代理:解决跨域的同时,承担静态资源托管、负载均衡、接口防护的作用;
- 后端 CORS 做兜底配置:防止 Nginx 配置失误(如漏配某个接口转发),或后端接口被直接访问时,通过严格的 CORS 规则(仅允许 Nginx 域名)进一步保障安全。