跨域问题及解决方案

跨域原因

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

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

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

同源策略 是浏览器的一种安全机制,限制不同源的网站之间进行无限制的资源访问或脚本交互,防止恶意网站通过脚本窃取其他网站的敏感数据(如 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 反向代理替代,仅在兼容老旧浏览器场景下偶尔使用。

相关推荐
TeamDev2 小时前
使用 Vue.js 构建 Java 桌面应用
java·前端·vue.js
持续升级打怪中2 小时前
Vue项目中Axios全面封装实战指南
前端·javascript·vue.js
heyCHEEMS2 小时前
为什么放弃 v-if 选择 v-show?为什么组件越用越卡?
前端
百罹鸟2 小时前
【react 高频面试题—核心原理篇】:useEffect 的依赖项如果是数组或对象(引用类型),会有什么问题?如何解决?
前端·react.js·面试
hibear2 小时前
Smart Ticker - 支持任意字符的高性能文本差异动画滚动组件
前端·vue.js·react.js
脱氧核糖核酸2 小时前
2026了你还只会写点prompt?从AI提示词到可控自动化的演进之路
前端
HabaraAi2 小时前
记一次发现 DataTransfer 的 getData 的有趣问题
前端
a17798877122 小时前
print.js打印
前端·javascript·html
小林攻城狮2 小时前
前端实时语音转写:原生 MediaRecorder API 实践
前端·vue.js