创见博客
前端跨域(浏览器的同源策略)
七崽爱吃小饼干2026/01/30阅读 2专栏 JavaScript

一、什么是前端跨域?

跨域的本质是浏览器的同源策略(Same-Origin Policy) 限制:浏览器为了安全,只允许当前页面请求「同源」的资源,当请求的资源和当前页面的「源」不一致时,就会触发跨域限制。

「同源」的判断标准是:协议、域名、端口 三者必须完全一致。 举个例子:当前页面地址是 http://www.example.com:8080,以下请求会被判定为跨域:

  • https://www.example.com:8080(协议不同:http vs https)
  • http://blog.example.com:8080(域名不同:www vs blog)
  • http://www.example.com:8081(端口不同:8080 vs 8081)

二、同源策略主要限制哪些方面?

同源策略的限制集中在「数据层面」,核心是防止恶意网站窃取其他网站的敏感数据,主要限制以下行为:

1. AJAX/fetch 请求跨域

这是最常见的跨域限制:前端通过 XMLHttpRequest 或 fetch 向不同源的后端接口发请求时,浏览器会拦截响应,控制台报错(如 Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy)。

2. DOM 访问限制

无法操作不同源页面的 DOM,比如:

  • 嵌入的跨域 iframe,父页面无法获取 iframe 内的 document、window 等对象;
  • 跨域的 window.open 打开的新窗口,无法互相访问数据。

3. Cookie/LocalStorage 等存储限制

不同源的页面无法共享 Cookie、LocalStorage、SessionStorage 等存储数据,比如 A 网站的 Cookie 无法被 B 网站读取。

4. 其他限制

  • 跨域脚本无法获取 indexedDB 数据;
  • WebSocket 虽然本身不严格跨域,但也受同源策略的间接限制(需服务端配合)。

三、跨域问题的解决方案

不同的跨域场景对应不同的解决方案,以下是最常用、最符合现代开发规范的方案:

方案1:CORS(跨域资源共享)—— 推荐首选

这是后端主导的解决方案,也是最标准、最安全的方式,核心是后端在响应头中添加允许跨域的配置。

原理

后端在返回响应时,设置 Access-Control-Allow-Origin 等响应头,明确告诉浏览器「允许哪些源访问该资源」。(控制权在后端,所以说是由后端主导。)

示例(以 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');
  // 允许携带Cookie(需配合前端withCredentials: true)
  res.setHeader('Access-Control-Allow-Credentials', 'true');
  // 允许的请求方法
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  // 允许的请求头
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
  
  // 处理预检请求(OPTIONS)
  if (req.method === 'OPTIONS') {
    return res.sendStatus(204); // 预检请求无需返回内容
  }
  next();
});

// 测试接口
app.get('/api/data', (req, res) => {
  res.json({ code: 200, data: '跨域请求成功' });
});

app.listen(8080, () => {
  console.log('服务运行在8080端口');
});
前端请求示例(fetch)
javascript
fetch('http://localhost:8080/api/data', {
  method: 'GET',
  credentials: 'include' // 携带Cookie(需后端允许)
})
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err));

方案2:前端代理(开发环境专用)

开发阶段(如 Vue/React 项目),可以通过本地代理绕过跨域限制,核心是「让前端请求先发到本地服务器,再由本地服务器转发到目标后端」(服务器之间请求无跨域限制)。

示例(Vue项目,vue.config.js)
javascript
module.exports = {
  devServer: {
    proxy: {
      // 匹配以/api开头的请求
      '/api': {
        target: 'http://localhost:8080', // 目标后端地址
        changeOrigin: true, // 开启跨域代理
        pathRewrite: { '^/api': '' } // 去掉请求路径中的/api前缀(可选)
      }
    }
  }
};

此时前端请求只需写本地路径:

javascript
// 实际请求会被代理到 http://localhost:8080/data
fetch('/api/data').then(res => res.json());

方案3:JSONP(仅支持GET请求,不推荐)

这是早期的跨域方案,利用 <script> 标签不受同源策略限制的特性,仅支持 GET 请求,安全性和灵活性较差,仅适用于老项目兼容。

示例

后端代码(Node.js):

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

前端代码:

javascript
function handleJsonpData(data) {
  console.log(data); // 接收后端返回的数据
}

// 动态创建script标签
const script = document.createElement('script');
script.src = 'http://localhost:8080/api/jsonp?callback=handleJsonpData';
document.body.appendChild(script);

方案4:其他补充方案

  • nginx反向代理:生产环境常用,通过nginx配置转发请求,原理和前端代理一致,由服务器层解决跨域;
  • postMessage:用于解决跨域页面间的通信(如iframe、多窗口),而非AJAX请求;
  • WebSocket:本身不受同源策略限制,可用于跨域实时通信。

总结

  1. 跨域本质:浏览器的同源策略限制,核心是保护用户数据安全,判断标准是「协议、域名、端口」三者一致;
  2. 主要限制:AJAX请求、DOM访问、Cookie/存储共享是最核心的限制点;
  3. 核心解决方案:生产环境优先用「CORS(后端配置)」,开发环境用「前端代理」,JSONP仅作为老项目兼容方案。
评论
0/100