前后端跨域处理全指南:Java后端+Vue前端完整解决方案

摘要:本文详细介绍跨域问题的产生原因、浏览器同源策略机制,以及基于Java后端和Vue前端技术栈的多种跨域处理方案。涵盖@CrossOrigin注解、全局CORS配置、过滤器、Spring Security集成、Vue代理配置、Nginx反向代理等多种方案,并提供完整可运行的代码示例,适用于初中级开发者学习参考。

一、跨域基础概念

1.1 什么是跨域?

跨域(Cross-Origin)是指浏览器出于安全考虑,限制从一个域(协议+域名+端口)加载的网页去请求另一个域的资源。当协议、域名或端口三者中有任意一项不同时,就会触发跨域限制。

示例

  • http://localhost:3000http://localhost:8080(端口不同)❌ 跨域
  • http://example.comhttps://example.com(协议不同)❌ 跨域
  • http://api.example.comhttp://www.example.com(子域名不同)❌ 跨域
  • http://localhost:3000http://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预检请求失败

排查步骤

  1. 打开浏览器开发者工具 → Network
  2. 查看请求的Response Headers
  3. 检查是否有Access-Control-Allow-Origin字段
  4. 检查OPTIONS请求是否返回204或200

问题2:Credential is not supported for wildcard origin

原因

  • 同时设置了allowCredentials=trueallowedOrigins="*"
  • 浏览器不允许通配符源携带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面板分析

  1. 打开开发者工具 → Network
  2. 勾选"Preserve log"
  3. 发送跨域请求
  4. 查看请求详情:
    • 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 核心要点

  1. 跨域是浏览器的安全机制,服务器之间无此限制
  2. CORS是最标准的解决方案,推荐后端配置
  3. 开发环境用代理,生产环境用Nginx
  4. allowCredentials=true时,allowedOrigins不能是"*"
  5. Spring Security需放行OPTIONS预检请求
  6. Nginx配置注意斜杠和always参数
  7. 携带Cookie需前后端同时配置

6.3 推荐架构

标准企业级项目

复制代码
开发环境:Vue DevServer代理 + 后端CORS全局配置
生产环境:Nginx反向代理 + 后端CORS全局配置(双重保障)
权限系统:Spring Security + Token认证

参考资料

相关推荐
Dylan的码园1 小时前
深入浅出Java排序:从基础算法到实战优化(上)
java·数据结构·算法
jason.zeng@15022072 小时前
基于数据库 + JWT 的 Spring Boot Security 完整示例
数据库·spring boot·oracle
颜淡慕潇2 小时前
深度解读 Spring Boot 3.5.9— 工程视角的稳健演进与价值释放
java·spring boot·后端·spring
夜郎king2 小时前
一文掌握:Java项目目录结构文档自动化生成
java·自动化·java原生目录生成
小二·2 小时前
Python Web 开发进阶实战:国际化(i18n)与多语言支持 —— Vue I18n + Flask-Babel 全栈解决方案
前端·vue.js·python
全栈前端老曹2 小时前
【包管理】npm最常见的10大问题故障和解决方案
前端·javascript·rust·npm·node.js·json·最佳实践
appearappear2 小时前
IntelliJ IDEA 2025.3.1 中 Export → SQL Updates 不带 WHERE 的真实原因与解决方案(OpenAI 协助整理)
java·数据库
玄〤2 小时前
黑马点评中的分布式锁设计与实现(Redis + Redisson)
java·数据库·redis·笔记·分布式·后端
码界奇点2 小时前
基于SpringBoot与Shiro的细粒度动态权限管理系统设计与实现
java·spring boot·后端·spring·毕业设计·源代码管理