前后端跨域处理全指南:Java 后端+Vue 前端完整解决方案
一、跨域基础概念
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 后端实现
单个接口跨域:
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!";
}
}
多个接口跨域:
@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)
// src/api/request.js
import axios from 'axios';
const request = axios.create({
baseURL: 'http://localhost:8080', // 后端地址
timeout: 5000
});
export default request;
<!-- 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 后端实现
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 实例配置:
// 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 封装:
// 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 后端实现
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):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
Security 配置类:
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 认证):
// 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;
登录示例:
// 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 项目):
// 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 项目):
// 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 配置
// 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 配置
基础配置(推荐):
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 时使用):
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;
}
}
动态允许源配置:
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 配置:
// 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 全局配置
理由:
- 前端独立开发,不依赖后端配置
- 快速迭代,方便调试
- 为生产环境做好准备
示例配置:
// 开发环境: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 认证
关键配置:
@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 管理:
// 请求拦截器:自动添加 Token
service.interceptors.request.use(config => {
const token = localStorage.getItem('token');
if (token) {
config.headers['Authorization'] = 'Bearer ' + token;
}
return config;
});
4.4 多租户/SAAS 系统
推荐方案:过滤器 Filter + 动态跨域配置
实现思路:
- 根据租户域名动态设置允许的源
- 支持租户级别的跨域白名单管理
- 从数据库或配置中心读取跨域规则
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
解决方案:
// 错误配置
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 未处理预检请求
解决方案:
// Security 放行 OPTIONS
http.authorizeHttpRequests(auth -> auth
.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll());
# Nginx 处理 OPTIONS
if ($request_method = OPTIONS) {
return 204;
}
5.2 Nginx 配置踩坑
坑 1:proxy_pass 的斜杠问题
# 错误:路径会保留/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 参数
# 错误:错误响应时不会添加跨域头
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属性设置不当
解决方案:
前端:
axios.get('/api/user', { withCredentials: true });
后端:
config.setAllowCredentials(true);
config.setAllowedOrigins(Arrays.asList("http://localhost:8080")); // 不能是*
Cookie 设置:
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
# 测试 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:临时禁用浏览器安全策略(仅用于调试)
# 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 认证
