跨域问题及解决方案

跨域原因

当前端请求地址与后端接口地址不同源时,浏览器会拦截接口请求,出现跨域问题。

同源:协议、域名、端口必须完全一致。

跨域:协议、域名、端口只要有一个不一致,就会跨域。

同源策略 是浏览器的一种安全机制,限制不同源的网站之间进行无限制的资源访问或脚本交互,防止恶意网站通过脚本窃取其他网站的敏感数据(如 Cookie、本地存储等)。

解决方案

前端配置代理

在项目根目录的 vite.config.ts 中配置 server.proxy

ts 复制代码
export default defineConfig({
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:3000', // 后端接口地址
        changeOrigin: true, // 允许跨域
        rewrite: (path) => path.replace(/^/api/, '') // 去掉 '/api' 前缀
      }
    }
  }
});

后端配置 CORS

CORS(跨域资源共享)是 W3C 定义的跨域解决方案,通过后端在响应头中添加 Access-Control-Allow-Origin 等字段,告诉浏览器 "允许前端的跨域请求"。

以 Node.js + Express 为例,配置方式如下:

js 复制代码
const express = require('express');
const cors = require('cors');
const app = express();

// 允许前端本地跨域请求
app.use(cors({
  origin: 'http://localhost:5173', // 前端开发服务器地址
  methods: ['GET', 'POST'], // 允许的请求方法
  allowedHeaders: ['Content-Type', 'Authorization'] // 允许的请求头
}));

// 或者更简单:允许所有源(建议开发环境临时使用,禁止生产环境使用)
// app.use(cors());

// 后端接口示例
app.get('/api/user', (req, res) => {
  res.send({ name: 'test' });
});

app.listen(3000, () => {
  console.log('后端服务运行在 3000 端口');
});

配置后,后端响应头会包含 Access-Control-Allow-Origin 等字段,浏览器识别到这些字段就会允许跨域请求。

Nginx 反向代理

生产环境中,前端请求发送到 Nginx 服务器(与前端同源),Nginx 再将请求转发到后端服务,后端响应数据也通过 Nginx 返回给前端。浏览器只与 Nginx 交互,不直接与后端通信,而服务器与服务器之间没有 "同源策略" 限制,也就不存在跨域问题。

nginx 复制代码
# 反向代理:所有以 /api 开头的请求转发到后端 3000 端口
location /api/ {
    proxy_pass http://127.0.0.1:3000;  # 后端服务地址(服务器本地)
    
    # 传递请求头信息(必须配置,否则后端可能无法获取正确的客户端信息)
    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;
}

JSONP

浏览器中有一些标签天生具有跨域能力,如:imglinkiframescript 等,JSONP 就是利用了 script 标签不受同源策略限制的特性。

  1. 前端提前定义回调函数 fn,并动态创建 script 标签,设置 src接口地址?callback=fn
  2. 后端获取请求参数 callback,返回该回调函数的调用字符串 'fn(data)'
  3. 浏览器获取到 script 标签的返回内容(即 'fn(data)')并解析执行,回调函数 fn 在前端被调用,就可以获取到后端返回的 data

前端示例:

js 复制代码
// 提前定义好的回调函数
function handleData(data) {
  console.log('获取到的跨域数据:', data);
}

// 动态创建 script 标签
const script = document.createElement('script');
script.src = 'http://localhost:3000/api/users?callback=handleData';

// 将 script 添加到 body,发送 get 请求
document.body.appendChild(script);

后端示例:

js 复制代码
app.get('/api/users', (req, res) => {
  // 获取回调函数名
  const callback = req.query.callback;
  const data = { code: 200, data: [{ id: 1, name: '张三' }] };
  // 返回 `回调函数(data)` 格式的字符串
  // 前端 script 标签拿到返回内容,会将字符串当作脚本执行,回调函数就会在前端被调用
  res.send(`${callback}(${JSON.stringify(data)})`);
});

JSONP 是传统方案,局限性大,仅支持 GET 请求(因为 script 标签只支持 GET 请求),且存在安全风险(可能遭受 XSS 攻击),已被 CORS 和 Nginx 反向代理替代,仅在兼容老旧浏览器场景下偶尔使用。

相关推荐
太平洋月光15 分钟前
AI 快捷指令:Cursor Rules · Commands · Skills
前端·ai编程
默_笙18 分钟前
😁 LLM 不知道的事它不会说"不知道",而是会"编"——RAG 就是让它闭嘴先查资料
前端·javascript
stringwu20 分钟前
用 LangGraph 落地飞书 Bug 自动修复 Agent 实践
前端
Revolution6120 分钟前
变量提升到底提升了什么:为什么 var 是 undefined,let 却直接报错
前端·javascript
Assby24 分钟前
为什么我不建议你在 MySQL 里写 `IN (超过1000个ID)`?从 AST 解析到存储引擎的深度拆解
后端·面试
labixiong25 分钟前
ES2026 落地了什么?两个真上线的特性 + 两个被毙的,一次讲清楚
javascript·ecmascript 6·ecmascript 8
Revolution6125 分钟前
本地明明正常,为什么 CI 又挂了:一次前端构建失败的排查过程
前端·前端工程化
太平洋月光32 分钟前
stagewise如何结合cursor开发
前端·ai编程
前端Hardy33 分钟前
JavaScript 终于又进化了!ES2026 正式发布,这些新特性你必须知道!
前端
光影少年34 分钟前
react navite 页面跳转、传参、路由监听、导航栏自定义
前端·react native·react.js