摘要:本文详细介绍跨域问题的产生原因、浏览器同源策略机制,以及基于Java后端和Vue前端技术栈的多种跨域处理方案。涵盖@CrossOrigin注解、全局CORS配置、过滤器、Spring Security集成、Vue代理配置、Nginx反向代理等多种方案,并提供完整可运行的代码示例,适用于初中级开发者学习参考。
一、跨域基础概念
1.1 什么是跨域?
跨域(Cross-Origin)是指浏览器出于安全考虑,限制从一个域(协议+域名+端口)加载的网页去请求另一个域的资源。当协议、域名或端口三者中有任意一项不同时,就会触发跨域限制。
示例:
http://localhost:3000→http://localhost:8080(端口不同)❌ 跨域http://example.com→https://example.com(协议不同)❌ 跨域http://api.example.com→http://www.example.com(子域名不同)❌ 跨域http://localhost:3000→http://localhost:3000/api(同源)✅ 不跨域
1.2 为什么会有跨域限制?
浏览器的同源策略(Same-Origin Policy,SOP) 是Web安全的核心机制,用于:
- 防止恶意网站读取另一个网站的敏感数据(如Cookie、localStorage)
- 防止CSRF(跨站请求伪造)攻击
- 保护用户隐私和数据安全
注意 :跨域限制仅存在于浏览器,服务器之间的HTTP请求(如Java HttpClient、Postman)不受此限制。
1.3 CORS(跨域资源共享)工作原理
CORS是W3C标准,通过服务器设置HTTP响应头告知浏览器允许哪些源访问资源。
简单请求流程:
浏览器请求 → 服务器返回Access-Control-Allow-Origin → 浏览器验证放行
复杂请求(预检请求)流程:
浏览器OPTIONS预检 → 服务器返回允许方法/头 → 浏览器发送真实请求 → 返回数据
二、完整的跨域处理方案列表
以下是适用于Java后端+Vue前端的6种核心方案:
| 方案 | 适用场景 | 实现复杂度 | 安全性 | 需要后端配合 |
|---|---|---|---|---|
| 1. @CrossOrigin注解 | 少量接口快速测试 | ⭐ 低 | 中 | ✅ 是 |
| 2. 全局CORS配置 | 企业级标准方案 | ⭐⭐ 中 | 高 | ✅ 是 |
| 3. 过滤器Filter | 动态权限控制 | ⭐⭐ 中 | 高 | ✅ 是 |
| 4. Spring Security集成 | 带权限系统的项目 | ⭐⭐⭐ 高 | 高 | ✅ 是 |
| 5. Vue DevServer代理 | 开发环境专用 | ⭐ 低 | 中 | ❌ 否 |
| 6. Nginx反向代理 | 生产环境部署 | ⭐⭐ 中 | 高 | ❌ 否 |
三、方案详解
方案1:@CrossOrigin注解(最简单)
3.1.1 实现原理
通过在Controller或方法上添加@CrossOrigin注解,Spring MVC会自动添加CORS响应头。
3.1.2 后端实现
单个接口跨域:
java
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DemoController {
// 允许指定域名的跨域请求
@CrossOrigin(origins = "http://localhost:8080")
@GetMapping("/api/demo")
public String demo() {
return "Hello from CORS!";
}
}
多个接口跨域:
java
@RestController
@RequestMapping("/api")
// 类级别注解:该类下所有方法都支持跨域
@CrossOrigin(
origins = {"http://localhost:8080", "http://localhost:3000"},
methods = {RequestMethod.GET, RequestMethod.POST},
maxAge = 3600
)
public class ApiController {
@GetMapping("/users")
public String getUsers() {
return "Users data";
}
@PostMapping("/login")
public String login() {
return "Login success";
}
}
3.1.3 前端配置(Vue + Axios)
javascript
// src/api/request.js
import axios from 'axios';
const request = axios.create({
baseURL: 'http://localhost:8080', // 后端地址
timeout: 5000
});
export default request;
vue
<!-- src/components/Demo.vue -->
<template>
<div>{{ message }}</div>
</template>
<script>
import request from '@/api/request';
export default {
data() {
return {
message: ''
}
},
async mounted() {
try {
const res = await request.get('/api/demo');
this.message = res.data;
} catch (error) {
console.error('跨域请求失败:', error);
}
}
}
</script>
3.1.4 优缺点分析
优点:
- 配置简单,一行注解搞定
- 适合少量接口或快速原型开发
缺点:
- 每个接口都需要单独配置,维护成本高
- 不适合大规模企业项目
- 无法统一管理跨域规则
方案2:全局CORS配置(推荐)
3.2.1 实现原理
实现WebMvcConfigurer接口,重写addCorsMappings方法,统一配置全局跨域规则。
3.2.2 后端实现
java
package com.example.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 全局CORS配置类
* 推荐用于企业级项目,统一管理跨域规则
*/
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 对所有路径生效
.allowedOrigins( // 允许的前端域名(生产环境需指定具体域名)
"http://localhost:8080",
"http://localhost:3000",
"https://www.example.com"
)
.allowedMethods( // 允许的HTTP方法
"GET", "POST", "PUT", "DELETE", "OPTIONS"
)
.allowedHeaders("*") // 允许的请求头
.exposedHeaders("X-Total-Count", "X-Page-Count") // 暴露的响应头
.allowCredentials(true) // 允许携带Cookie(需与allowedOrigins配合)
.maxAge(3600); // 预检请求缓存时间(秒)
}
}
3.2.3 前端配置
Axios实例配置:
javascript
// src/utils/request.js
import axios from 'axios';
const service = axios.create({
baseURL: 'http://localhost:8080/api', // 后端API地址
timeout: 10000,
withCredentials: true // 携带Cookie(需后端allowCredentials=true)
});
// 请求拦截器:添加Token等
service.interceptors.request.use(
config => {
const token = localStorage.getItem('token');
if (token) {
config.headers['Authorization'] = 'Bearer ' + token;
}
return config;
},
error => {
return Promise.reject(error);
}
);
// 响应拦截器:统一处理错误
service.interceptors.response.use(
response => response,
error => {
if (error.response) {
switch (error.response.status) {
case 401:
console.error('未授权,请登录');
break;
case 403:
console.error('拒绝访问');
break;
case 500:
console.error('服务器错误');
break;
}
}
return Promise.reject(error);
}
);
export default service;
API封装:
javascript
// src/api/user.js
import request from '@/utils/request';
export const getUserList = (params) => {
return request({
url: '/users',
method: 'get',
params
});
};
export const createUser = (data) => {
return request({
url: '/users',
method: 'post',
data
});
};
3.2.4 优缺点分析
优点:
- 统一管理,便于维护
- 一次配置,全局生效
- 支持精细化的权限控制
缺点:
- 配置相对复杂
- 需要理解CORS相关配置项
方案3:过滤器Filter(灵活控制)
3.3.1 实现原理
通过自定义Filter,在请求链中手动设置CORS响应头,可实现动态的跨域逻辑。
3.3.2 后端实现
java
package com.example.filter;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* CORS过滤器
* 适用于需要动态判断跨域权限的场景
*/
@Component
public class CorsFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String origin = request.getHeader("Origin");
// 动态判断是否允许该源跨域(可根据数据库、Redis等动态配置)
if (isAllowedOrigin(origin)) {
response.setHeader("Access-Control-Allow-Origin", origin);
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Max-Age", "3600");
}
// 处理OPTIONS预检请求
if ("OPTIONS".equals(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
return;
}
filterChain.doFilter(request, response);
}
/**
* 判断源是否在白名单中(可改为从数据库查询)
*/
private boolean isAllowedOrigin(String origin) {
if (origin == null) {
return false;
}
// 允许的源列表(实际项目中可从配置文件或数据库读取)
String[] allowedOrigins = {
"http://localhost:8080",
"http://localhost:3000",
"https://www.example.com"
};
for (String allowed : allowedOrigins) {
if (allowed.equals(origin)) {
return true;
}
}
return false;
}
}
3.3.3 前端配置
同方案2,使用Axios配置即可。
3.3.4 优缺点分析
优点:
- 灵活性最高,可实现动态跨域控制
- 可根据请求参数、用户角色等动态判断是否允许跨域
- 适用于多租户、SAAS系统等复杂场景
缺点:
- 实现相对复杂
- 需要手动维护CORS响应头逻辑
- 性能略低于Spring内置CORS支持
方案4:Spring Security集成(带权限系统)
3.4.1 实现原理
在Spring Security安全框架中集成CORS,确保跨域请求在经过安全链时正确处理。
3.4.2 后端实现
依赖配置(pom.xml):
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
Security配置类:
java
package com.example.config;
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;
import java.util.Arrays;
/**
* Spring Security + CORS 配置
* 关键:CORS必须在Security之前处理
*/
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// 启用CORS(关键步骤)
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
// 禁用CSRF(前后端分离项目通常禁用)
.csrf(csrf -> csrf.disable())
// 配置权限规则
.authorizeHttpRequests(auth -> auth
// 放行OPTIONS预检请求
.requestMatchers(org.springframework.http.HttpMethod.OPTIONS, "/**").permitAll()
// 公开接口
.requestMatchers("/api/public/**").permitAll()
// 需要认证的接口
.anyRequest().authenticated()
);
return http.build();
}
/**
* CORS配置源
* Security会自动读取此配置
*/
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
// 允许的源(生产环境必须指定具体域名)
configuration.setAllowedOrigins(Arrays.asList(
"http://localhost:8080",
"http://localhost:3000",
"https://www.example.com"
));
// 允许的方法
configuration.setAllowedMethods(Arrays.asList(
"GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"
));
// 允许的请求头
configuration.setAllowedHeaders(Arrays.asList("*"));
// 允许携带凭证
configuration.setAllowCredentials(true);
// 暴露的响应头
configuration.setExposedHeaders(Arrays.asList("X-Total-Count", "Authorization"));
// 预检缓存时间
configuration.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
3.4.3 前端配置
Axios配置(带Token认证):
javascript
// src/utils/request.js
import axios from 'axios';
import { Message } from 'element-ui';
const service = axios.create({
baseURL: 'http://localhost:8080',
timeout: 10000,
withCredentials: true // 携带Cookie(如SessionId)
});
// 请求拦截器
service.interceptors.request.use(
config => {
// 从localStorage获取Token
const token = localStorage.getItem('access_token');
if (token) {
config.headers['Authorization'] = 'Bearer ' + token;
}
return config;
},
error => {
return Promise.reject(error);
}
);
// 响应拦截器
service.interceptors.response.use(
response => {
const res = response.data;
// 根据业务状态码处理
if (res.code !== 200) {
Message.error(res.message || '请求失败');
// 401未授权,跳转登录页
if (res.code === 401) {
localStorage.removeItem('access_token');
window.location.href = '/login';
}
return Promise.reject(new Error(res.message || 'Error'));
} else {
return res;
}
},
error => {
console.error('请求错误:', error);
// 401未授权
if (error.response && error.response.status === 401) {
Message.error('未授权,请重新登录');
localStorage.removeItem('access_token');
window.location.href = '/login';
}
return Promise.reject(error);
}
);
export default service;
登录示例:
javascript
// src/api/auth.js
import request from '@/utils/request';
export const login = (data) => {
return request({
url: '/api/auth/login',
method: 'post',
data
});
};
export const getUserInfo = () => {
return request({
url: '/api/user/info',
method: 'get'
});
};
3.4.4 优缺点分析
优点:
- 完美集成Spring Security安全体系
- 支持Token认证、Session认证等多种认证方式
- 适用于需要权限控制的复杂业务系统
缺点:
- 配置复杂度较高
- 需要理解Spring Security的工作原理
- 新手容易出错
方案5:Vue DevServer代理(开发环境)
3.5.1 实现原理
利用Webpack DevServer内置的http-proxy-middleware,在开发服务器层面对请求进行转发,绕过浏览器的同源限制。
3.5.2 前端配置
vue.config.js配置(Vue CLI项目):
javascript
// vue.config.js
module.exports = {
devServer: {
port: 8080, // 前端开发服务器端口
host: '0.0.0.0',
open: true,
proxy: {
// 代理规则1:/api开头的请求
'/api': {
target: 'http://localhost:8080', // 后端真实地址
changeOrigin: true, // 修改请求头中的Origin为目标地址
ws: true, // 支持WebSocket代理
pathRewrite: {
'^/api': '' // 去掉/api前缀(如果后端接口没有/api)
},
// 可选:添加自定义请求头
headers: {
'X-Dev-Proxy': 'true'
}
},
// 代理规则2:/upload开头的请求
'/upload': {
target: 'http://localhost:9090',
changeOrigin: true,
pathRewrite: {
'^/upload': '/files'
}
}
}
}
}
vite.config.js配置(Vite项目):
javascript
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
server: {
port: 5173,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}
})
3.5.3 Axios配置
javascript
// src/utils/request.js
import axios from 'axios';
const service = axios.create({
// 开发环境使用相对路径(会被代理转发)
// 生产环境需配置为真实的后端地址
baseURL: process.env.NODE_ENV === 'development' ? '/api' : 'http://api.example.com',
timeout: 10000
});
export default service;
环境变量配置(.env.development):
NODE_ENV=development
VUE_APP_BASE_API=/api
环境变量配置(.env.production):
NODE_ENV=production
VUE_APP_BASE_API=https://api.example.com
3.5.4 优缺点分析
优点:
- 开发环境最简单、最快捷的方案
- 无需后端配合,前端独立完成
- 支持WebSocket代理
缺点:
- 仅适用于开发环境,生产环境不可用
- 生产部署时需要修改baseURL或使用其他方案
- 调试时代理转发可能影响问题定位
方案6:Nginx反向代理(生产环境)
3.6.1 实现原理
通过Nginx作为反向代理服务器,前端请求Nginx同源地址,Nginx转发到后端。对浏览器而言是同源请求,完美解决跨域问题。
3.6.2 Nginx配置
基础配置(推荐):
nginx
server {
listen 80;
server_name www.example.com;
# 前端静态资源
location / {
root /usr/share/nginx/html; # Vue打包后的dist目录
index index.html;
try_files $uri $uri/ /index.html; # 解决Vue路由刷新404
}
# 后端API代理
location /api/ {
# 转发到后端真实地址
proxy_pass http://localhost:8080/;
# 关键:传递原始Host和客户端IP
proxy_set_header Host $proxy_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;
# WebSocket支持
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
完整CORS配置(后端不配置CORS时使用):
nginx
server {
listen 80;
server_name api.example.com;
location / {
# 允许的源(生产环境指定具体域名)
add_header 'Access-Control-Allow-Origin' 'https://www.example.com' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, X-Requested-With' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Max-Age' '3600' always;
# 处理OPTIONS预检请求
if ($request_method = OPTIONS) {
return 204;
}
# 反向代理到后端
proxy_pass http://localhost: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;
}
}
动态允许源配置:
nginx
server {
listen 80;
server_name api.example.com;
# 动态设置允许的源
set $cors_origin "";
if ($http_origin ~* ^(https?://(localhost|www\.example\.com)(:[0-9]+)?$)) {
set $cors_origin $http_origin;
}
location / {
# 使用变量设置允许的源
add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
if ($request_method = OPTIONS) {
return 204;
}
proxy_pass http://localhost:8080;
}
}
3.6.3 前端配置
Axios配置:
javascript
// src/utils/request.js
import axios from 'axios';
// 生产环境使用Nginx代理的地址(同域)
const service = axios.create({
baseURL: process.env.NODE_ENV === 'production'
? '/api' // 生产环境:Nginx代理地址(同域)
: 'http://localhost:8080', // 开发环境:使用后端直连或开发代理
timeout: 10000,
withCredentials: true
});
export default service;
3.6.4 优缺点分析
优点:
- 生产环境最推荐方案
- 前后端同域,天然避免跨域问题
- 支持负载均衡、缓存、HTTPS等高级功能
- 无需修改后端代码
缺点:
- 需要额外的Nginx服务器
- 配置相对复杂
- 需要运维知识
四、最佳实践建议
4.1 开发环境
推荐方案:Vue DevServer代理 + 后端CORS全局配置
理由:
- 前端独立开发,不依赖后端配置
- 快速迭代,方便调试
- 为生产环境做好准备
示例配置:
javascript
// 开发环境:vue.config.js
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
pathRewrite: { '^/api': '' }
}
}
}
}
4.2 生产环境
推荐方案:Nginx反向代理 + 后端CORS全局配置(双重保障)
理由:
- Nginx作为流量入口,统一处理跨域
- 后端CORS作为备用,应对直接访问后端的情况
- 支持多环境部署(开发、测试、生产)
架构图:
浏览器
↓
Nginx (80/443)
├─ / → 前端静态资源 (同域)
└─ /api → 后端API (同域代理)
↓
Spring Boot (8080)
4.3 复杂权限系统
推荐方案:Spring Security + CORS + Token认证
关键配置:
java
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.cors().and()
.csrf().disable()
.authorizeHttpRequests(auth -> auth
.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.requestMatchers("/api/public/**").permitAll()
.anyRequest().authenticated()
);
return http.build();
}
前端Token管理:
javascript
// 请求拦截器:自动添加Token
service.interceptors.request.use(config => {
const token = localStorage.getItem('token');
if (token) {
config.headers['Authorization'] = 'Bearer ' + token;
}
return config;
});
4.4 多租户/SAAS系统
推荐方案:过滤器Filter + 动态跨域配置
实现思路:
- 根据租户域名动态设置允许的源
- 支持租户级别的跨域白名单管理
- 从数据库或配置中心读取跨域规则
java
private boolean isAllowedOrigin(String origin, String tenantId) {
// 从数据库查询该租户的跨域白名单
List<String> allowedOrigins = tenantService.getCorsWhiteList(tenantId);
return allowedOrigins.contains(origin);
}
五、常见问题排查
5.1 配置失败的典型原因
问题1:No 'Access-Control-Allow-Origin' header is present
原因:
- 后端未配置CORS
- Nginx未添加跨域头
- OPTIONS预检请求失败
排查步骤:
- 打开浏览器开发者工具 → Network
- 查看请求的Response Headers
- 检查是否有
Access-Control-Allow-Origin字段 - 检查OPTIONS请求是否返回204或200
问题2:Credential is not supported for wildcard origin
原因:
- 同时设置了
allowCredentials=true和allowedOrigins="*" - 浏览器不允许通配符源携带Cookie
解决方案:
java
// 错误配置
config.setAllowCredentials(true);
config.setAllowedOrigins(Arrays.asList("*"));
// 正确配置
config.setAllowCredentials(true);
config.setAllowedOrigins(Arrays.asList("http://localhost:8080"));
问题3:OPTIONS预检请求返回404/403
原因:
- 后端未实现OPTIONS接口
- Security拦截了OPTIONS请求
- Nginx未处理预检请求
解决方案:
java
// Security放行OPTIONS
http.authorizeHttpRequests(auth -> auth
.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
);
nginx
# Nginx处理OPTIONS
if ($request_method = OPTIONS) {
return 204;
}
5.2 Nginx配置踩坑
坑1:proxy_pass的斜杠问题
nginx
# 错误:路径会保留/api
location /api/ {
proxy_pass http://localhost:8080; # 请求 /api/user → http://localhost:8080/api/user
}
# 正确:去掉/api前缀
location /api/ {
proxy_pass http://localhost:8080/; # 请求 /api/user → http://localhost:8080/user
}
坑2:缺少always参数
nginx
# 错误:错误响应时不会添加跨域头
add_header 'Access-Control-Allow-Origin' 'http://localhost:8080';
# 正确:所有响应都添加跨域头
add_header 'Access-Control-Allow-Origin' 'http://localhost:8080' always;
5.3 Cookie携带问题
现象:请求未携带Cookie或Set-Cookie不生效
原因:
- 前端未设置
withCredentials: true - 后端未设置
allowCredentials: true - Cookie的
SameSite属性设置不当
解决方案:
前端:
javascript
axios.get('/api/user', { withCredentials: true });
后端:
java
config.setAllowCredentials(true);
config.setAllowedOrigins(Arrays.asList("http://localhost:8080")); // 不能是*
Cookie设置:
java
Cookie cookie = new Cookie("token", tokenValue);
cookie.setHttpOnly(true);
cookie.setSecure(true); // HTTPS必需
cookie.setPath("/");
// 如果需要跨域,设置SameSite为None
cookie.setAttribute("SameSite", "None");
response.addCookie(cookie);
5.4 调试技巧
技巧1:使用curl测试CORS
bash
# 测试OPTIONS预检
curl -X OPTIONS -H "Origin: http://localhost:8080" \
-H "Access-Control-Request-Method: GET" \
-v http://localhost:8080/api/user
# 查看响应头
curl -I http://localhost:8080/api/user
技巧2:浏览器Network面板分析
- 打开开发者工具 → Network
- 勾选"Preserve log"
- 发送跨域请求
- 查看请求详情:
- Request Headers:检查Origin、Access-Control-Request-Method
- Response Headers:检查Access-Control-Allow-*
- Console:查看具体错误信息
技巧3:临时禁用浏览器安全策略(仅用于调试)
bash
# Chrome启动参数(仅开发调试使用,生产环境禁止)
chrome.exe --disable-web-security --user-data-dir="C:/temp/chrome-dev"
六、总结
6.1 方案对比表
| 方案 | 适用场景 | 推荐度 | 生产可用性 |
|---|---|---|---|
| @CrossOrigin注解 | 少量接口测试 | ⭐⭐ | ✅ 是 |
| 全局CORS配置 | 企业级标准方案 | ⭐⭐⭐⭐⭐ | ✅ 是 |
| 过滤器Filter | 动态权限控制 | ⭐⭐⭐⭐ | ✅ 是 |
| Spring Security集成 | 带权限系统 | ⭐⭐⭐⭐⭐ | ✅ 是 |
| Vue DevServer代理 | 开发环境 | ⭐⭐⭐⭐⭐ | ❌ 否 |
| Nginx反向代理 | 生产环境 | ⭐⭐⭐⭐⭐ | ✅ 是 |
6.2 核心要点
- 跨域是浏览器的安全机制,服务器之间无此限制
- CORS是最标准的解决方案,推荐后端配置
- 开发环境用代理,生产环境用Nginx
- allowCredentials=true时,allowedOrigins不能是"*"
- Spring Security需放行OPTIONS预检请求
- Nginx配置注意斜杠和always参数
- 携带Cookie需前后端同时配置
6.3 推荐架构
标准企业级项目:
开发环境:Vue DevServer代理 + 后端CORS全局配置
生产环境:Nginx反向代理 + 后端CORS全局配置(双重保障)
权限系统:Spring Security + Token认证