跨域方案汇总

一、先理解核心概念:什么是跨域?

跨域是浏览器的同源策略导致的安全限制:当请求的协议(http/https)、域名、端口三者任意一个与当前页面不一致时,就会触发跨域拦截。比如:

  • http://localhost:3000 访问 http://localhost:8080(端口不同)
  • http://www.a.com 访问 http://api.a.com(子域名不同)
  • http://a.com 访问 https://a.com(协议不同)

二、跨域请求的主流解决方案

1. CORS(跨域资源共享)- 最推荐的方案

核心原理:后端在响应头中添加允许跨域的配置,明确告知浏览器哪些域名、请求方法、头信息可以访问资源,是 W3C 标准,也是现代浏览器最支持的方案。

适用场景:前后端分离项目(Vue/React+Node/Java/PHP 等),后端可修改的场景。

实现示例

  • 后端配置(以 Node.js/Express 为例)

    javascript 复制代码
    const express = require('express');
    const app = express();
    
    // 全局跨域中间件
    app.use((req, res, next) => {
      // 允许指定域名跨域(* 表示允许所有,生产环境不推荐)
      res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000');
      // 允许的请求方法
      res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
      // 允许的自定义请求头
      res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
      // 允许携带cookie(需前后端同时配置)
      res.setHeader('Access-Control-Allow-Credentials', 'true');
      // 预检请求(OPTIONS)的缓存时间,避免重复发送
      res.setHeader('Access-Control-Max-Age', '86400');
    
      // 处理预检请求(OPTIONS)
      if (req.method === 'OPTIONS') {
        return res.sendStatus(200);
      }
      next();
    });
    
    // 接口示例
    app.get('/api/data', (req, res) => {
      res.json({ code: 200, data: '跨域成功' });
    });
    
    app.listen(8080, () => console.log('后端服务运行在8080端口'));
  • 前端请求(以 Axios 为例)

    javascript 复制代码
    import axios from 'axios';
    
    axios({
      url: 'http://localhost:8080/api/data',
      method: 'GET',
      withCredentials: true, // 如需携带cookie,必须开启
    }).then(res => console.log(res.data));

2. 代理服务器 - 开发环境首选

核心原理:浏览器有跨域限制,但服务器之间没有。通过本地开发服务器(如 Webpack Dev Server、Vite)做代理,将前端请求转发到目标后端,规避跨域问题。

适用场景:本地开发阶段(生产环境需配置 Nginx 代理)。

实现示例

  • Vite 配置(vite.config.js)

    javascript 复制代码
    export default {
      server: {
        proxy: {
          // 匹配以/api开头的请求
          '/api': {
            target: 'http://localhost:8080', // 目标后端地址
            changeOrigin: true, // 开启跨域代理
            // rewrite: (path) => path.replace(/^/api/, ''), // 可选:去掉/api前缀
          },
        },
      },
    };
  • 前端请求(无需写完整域名)

    javascript 复制代码
    axios.get('/api/data').then(res => console.log(res.data));
  • 生产环境 Nginx 配置

    nginx 复制代码
    server {
      listen 80;
      server_name www.frontend.com;
    
      # 代理后端接口
      location /api/ {
        proxy_pass http://localhost:8080/api/; # 转发到后端地址
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
      }
    
      # 前端静态资源
      location / {
        root /usr/share/nginx/html;
        index index.html;
        try_files $uri $uri/ /index.html; # 适配SPA路由
      }
    }

3. JSONP - 兼容老旧浏览器

核心原理 :利用<script>标签不受同源策略限制的特性,通过动态创建 script 标签请求后端接口,后端返回一段带回调函数的 JS 代码,前端执行回调获取数据。

局限:仅支持 GET 请求,安全性较低(可能存在 XSS 风险),仅推荐兼容老旧浏览器时使用。

实现示例

  • 后端(Node.js/Express)

    javascript 复制代码
    app.get('/api/jsonp', (req, res) => {
      const { callback } = req.query; // 获取前端传的回调函数名
      const data = { code: 200, data: 'JSONP跨域成功' };
      // 返回回调函数+数据(格式:callback(data))
      res.send(`${callback}(${JSON.stringify(data)})`);
    });
  • 前端

    javascript 复制代码
    function jsonpRequest(url, callbackName) {
      return new Promise((resolve) => {
        // 1. 创建script标签
        const script = document.createElement('script');
        script.src = `${url}?callback=${callbackName}`;
        // 2. 定义回调函数
        window[callbackName] = (data) => {
          resolve(data);
          document.body.removeChild(script); // 执行完移除script
          delete window[callbackName]; // 清理全局函数
        };
        // 3. 插入到页面
        document.body.appendChild(script);
      });
    }
    
    // 调用
    jsonpRequest('http://localhost:8080/api/jsonp', 'handleJsonp').then(res => {
      console.log(res);
    });

4. postMessage - 页面间跨域通信

核心原理:HTML5 新增的 API,允许不同域名的页面(如 iframe、多窗口)之间安全地传递数据,不是用于 AJAX 请求,而是页面间通信。

适用场景:iframe 嵌套跨域页面、多窗口通信。

实现示例

  • 父页面(a.com

    html 复制代码
    <iframe id="iframe" src="http://b.com"></iframe>
    <script>
      const iframe = document.getElementById('iframe');
      // 等子页面加载完成后发送消息
      iframe.onload = () => {
        iframe.contentWindow.postMessage(
          { type: 'data', content: '来自a.com的消息' },
          'http://b.com' // 只允许发送给该域名,* 表示所有
        );
      };
      // 接收子页面的回复
      window.addEventListener('message', (e) => {
        if (e.origin === 'http://b.com') { // 验证来源,防止恶意消息
          console.log('收到回复:', e.data);
        }
      });
    </script>
  • 子页面(b.com

    javascript 复制代码
    // 接收父页面消息
    window.addEventListener('message', (e) => {
      if (e.origin === 'http://a.com') {
        console.log('收到消息:', e.data);
        // 回复父页面
        e.source.postMessage({ type: 'reply', content: '已收到消息' }, e.origin);
      }
    });

5. 其他补充方案

  • WebSocket:不受同源策略限制,适用于实时通信(如聊天、推送),协议是 ws/wss,而非 http。
  • document.domain :仅适用于主域名相同、子域名不同 的场景(如a.comapi.a.com),需双方页面设置document.domain = 'a.com',但兼容性差,不推荐。
  • CORS 的预检请求:PUT/DELETE/ 带自定义头的 POST 请求会先发送 OPTIONS 预检请求,后端需正确处理(如上面 CORS 示例中的 OPTIONS 逻辑)。

总结

  1. 核心推荐方案 :开发环境用代理服务器 ,生产环境用CORS (后端配置)+ Nginx 代理,覆盖 99% 的场景;
  2. 特殊场景 :老旧浏览器兼容用JSONP ,页面间通信用postMessage ,实时通信用WebSocket
  3. 关键原则:跨域的本质是浏览器的限制,服务器之间无跨域,因此 "代理" 和 "后端配置 CORS" 是最根本的解决思路。

选择方案时优先看场景:能改后端就用 CORS,开发阶段用代理,仅 GET 请求且需兼容老浏览器才用 JSONP。

相关推荐
风象南3 小时前
纯文本模型竟然也能直接“画图”,而且还很好用
前端·人工智能·后端
IT_陈寒3 小时前
Vite vs Webpack:5个让你的开发效率翻倍的实战对比
前端·人工智能·后端
wuhen_n4 小时前
TypeScript 强力护航:PropType 与组件事件类型的声明
前端·javascript·vue.js
wuhen_n4 小时前
组件设计原则:如何设计一个高内聚、低耦合的 Vue 组件
前端·javascript·vue.js
Moment16 小时前
Vibe Coding 时代,到底该选什么样的工具来提升效率❓❓❓
前端·后端·github
IT_陈寒17 小时前
SpringBoot性能飙升200%?这5个隐藏配置你必须知道!
前端·人工智能·后端
小时前端18 小时前
React性能优化的完整方法论,附赠大厂面试通关技巧
前端·react.js
Nicko18 小时前
Jetpack Compose BOM 2026.02.01 解读与升级指南
前端
小蜜蜂dry18 小时前
nestjs学习 - 控制器、提供者、模块
前端·node.js·nestjs