JavaScript篇:为什么你的前端请求被拒之门外?深入理解浏览器的同源策略

216 阅读5分钟

        大家好,我是江城开朗的豌豆,一名拥有6年以上前端开发经验的工程师。我精通HTML、CSS、JavaScript等基础前端技术,并深入掌握Vue、React、Uniapp、Flutter等主流框架,能够高效解决各类前端开发问题。在我的技术栈中,除了常见的前端开发技术,我还擅长3D开发,熟练使用Three.js进行3D图形绘制,并在虚拟现实与数字孪生技术上积累了丰富的经验,特别是在虚幻引擎开发方面,有着深入的理解和实践。

        我一直认为技术的不断探索和实践是进步的源泉,近年来,我深入研究大数据算法的应用与发展,尤其在数据可视化和交互体验方面,取得了显著的成果。我也注重与团队的合作,能够有效地推动项目的进展和优化开发流程。现在,我担任全栈工程师,拥有CSDN博客专家认证及阿里云专家博主称号,希望通过分享我的技术心得与经验,帮助更多人提升自己的技术水平,成为更优秀的开发者。

引言:一个让人抓狂的前端问题

最近我在做一个项目,需要从 https://www.my-site.com 向 https://api.other-site.com 发送请求获取数据。结果,浏览器无情地拒绝了我的请求,并抛出一个错误:

Access to fetch at 'https://api.other-site.com/data' from origin 'https://www.my-site.com' has been blocked by CORS policy...

相信不少前端开发者都遇到过类似的报错。为什么浏览器要阻止我的请求?  这背后就是 同源策略(Same-Origin Policy, SOP)  在起作用。今天,我们就来深入聊聊这个机制,理解它的原理,并学会如何合理绕过它。


1. 什么是同源策略?

同源策略是浏览器最核心的安全机制之一,它限制了 不同源的网页脚本 如何互相访问数据。简单来说,浏览器只允许来自相同源的脚本自由交互,而不同源的交互会受到严格限制

什么是"同源"?

两个 URL 如果满足以下三个条件,就是同源的:

  1. 协议(Protocol)  相同(http / https
  2. 域名(Domain)  相同(www.example.com
  3. 端口(Port)  相同(默认 80 或 443

示例:判断是否同源

URL 1URL 2是否同源原因
https://example.comhttps://example.com/api✅ 是协议、域名、端口相同
http://example.comhttps://example.com❌ 否协议不同(HTTP vs HTTPS)
https://example.comhttps://api.example.com❌ 否域名不同(主域 vs 子域)
https://example.com:8080https://example.com❌ 否端口不同(8080 vs 443)

2. 同源策略限制了哪些操作?

同源策略主要限制以下几种行为:

  1. AJAX / Fetch 跨域请求

    • 默认情况下,XMLHttpRequest 和 fetch() 不能访问不同源的资源。
    • 除非目标服务器明确允许(通过 CORS)。
  2. DOM 访问限制

    • 不同源的 iframe 不能直接访问其 contentDocument(除非设置 document.domain 或使用 postMessage)。
  3. Cookie / LocalStorage / IndexedDB 隔离

    • 不同源的网站无法互相读取对方的存储数据。

3. 为什么需要同源策略?

假设没有同源策略,会发生什么?

  • 恶意网站可以窃取你的数据
    比如,你登录了 https://your-bank.com,然后访问了一个恶意网站 https://evil-site.com,它可以通过 AJAX 偷偷访问你的银行数据。
  • CSRF(跨站请求伪造)攻击更容易发生
    攻击者可以在你不知情的情况下,利用你的登录态向银行发起转账请求。

同源策略就像是浏览器的一道安全门,防止恶意网站随意访问你的数据。


4. 如何绕过同源策略?(合法方式)

虽然同源策略很重要,但在实际开发中,我们经常需要跨域访问资源。以下是几种常见的解决方案:

(1)CORS(跨域资源共享)

CORS 是官方推荐的跨域方案,需要后端配合。服务器在响应头中添加 Access-Control-Allow-Origin 来允许特定源的请求。

示例(后端设置):

Access-Control-Allow-Origin: https://www.my-site.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type

前端直接使用 fetch

const response = await fetch('https://api.other-site.com/data');
const data = await response.json();

(2)JSONP(仅限 GET 请求)

JSONP 利用 <script> 标签不受同源策略限制的特性,适用于老式 API。

前端代码:

function handleResponse(data) {
  console.log(data);
}

const script = document.createElement('script');
script.src = 'https://api.other-site.com/data?callback=handleResponse';
document.body.appendChild(script);

后端返回:

handleResponse({ "name": "我", "age": 25 });

(3)代理服务器

如果你的后端不支持 CORS,可以自己搭建一个代理服务器:

// Node.js 代理示例(Express)
app.get('/proxy/data', async (req, res) => {
  const response = await fetch('https://api.other-site.com/data');
  const data = await response.json();
  res.json(data);
});

前端调用:

const response = await fetch('/proxy/data');
const data = await response.json();

(4)document.domain(仅适用于相同主域)

如果两个页面属于同一主域(如 a.example.com 和 b.example.com),可以设置:

document.domain = 'example.com';

这样它们就可以互相访问 DOM。


5. 实际开发中的坑与解决方案

(1)开发环境跨域问题

在本地开发时,前端运行在 http://localhost:3000,但 API 在 https://api.example.com,如何解决?

方案 1:配置 Webpack DevServer 代理

// vue.config.js / webpack.config.js
module.exports = {
  devServer: {
    proxy: {
      '/api': {
        target: 'https://api.example.com',
        changeOrigin: true,
      }
    }
  }
};

方案 2:浏览器禁用安全策略(仅限测试)
Chrome 启动时加参数:

chrome.exe --disable-web-security --user-data-dir="C:/Temp"

(2)withCredentials 携带 Cookie

如果跨域请求需要携带 Cookie,必须设置:

fetch('https://api.example.com/data', {
  credentials: 'include'
});

同时,后端必须设置:

Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: https://www.my-site.com  // 不能是 *

6. 总结

同源策略是浏览器安全的核心机制,但它也给前端开发带来了一些挑战。我们可以通过:

  • CORS(推荐)
  • JSONP(老项目兼容)
  • 代理服务器(灵活可控)
  • document.domain(同主域适用)

来合法绕过限制。理解这些机制,能让你在前端开发中更游刃有余地处理跨域问题。

你在项目中遇到过哪些跨域问题?欢迎在评论区分享你的经历!  🚀