一针见血!前端新手也能秒懂浏览器的同源策略

178 阅读4分钟

想象一下:你写了一个网页 https://www.yoursite.com,想用 JavaScript 获取 https://api.othersite.com 的数据。代码看着没问题,但浏览器无情地抛出一个错误:跨域请求被阻止!这就是同源策略(Same-Origin Policy) 在守护安全大门。


一、什么是“源”?—— 你的网络身份证

浏览器的“源”由三块拼图组成:

  1. 协议 (Protocol)http://, https://, ftp://
  2. 域名 (Host)www.example.com, api.service.org
  3. 端口 (Port):80 (HTTP默认), :443 (HTTPS默认), :8080

只有这三者完全相同,才属于“同源”!

同源示例:

  • https://shop.com/page1https://shop.com/page2 (协议、域名、端口相同)
  • http://localhost:8080/apphttp://localhost:8080/api (协议、域名、端口相同)

不同源示例:

  • https://shop.com vs http://shop.com (协议不同:HTTPS vs HTTP)
  • https://shop.com vs https://api.shop.com (域名不同:shop.com vs api.shop.com)
  • https://shop.com vs https://shop.com:8443 (端口不同:443 vs 8443)

二、同源策略管什么?—— 浏览器的安全围栏

同源策略的核心目标是:防止恶意网站窃取你的数据或冒充你的身份! 它主要限制以下行为:

  1. 读取非同源 DOM:

    • 你的脚本 (https://your-site.com) 无法直接读取或修改 https://bank.com 登录页面的 DOM 结构(比如获取密码输入框的值)。想象一下,如果允许,恶意网站就能偷看你在银行网站的输入!
  2. 发送非同源 AJAX/Fetch 请求 (默认):

    • 你的脚本默认不能向 https://api.other-site.com 发送 XMLHttpRequestfetch 请求并读取响应内容。这是最常见引发跨域错误的地方。
  3. 读写非同源的 Cookie、LocalStorage 等:

    • 你网站 https://site-a.com 设置的 Cookie,https://site-b.com 的脚本无法读取或修改。防止恶意网站盗用你的登录状态。

三、安全围栏的“门缝”—— 允许的跨域加载

同源策略不是铁板一块!以下资源默认允许跨域加载(仅加载,JS 通常无法直接操作内容):

  1. <img src="..."> 图片
  2. <link rel="stylesheet" href="..."> CSS 样式表
  3. <script src="..."> 脚本 (注意:加载的脚本运行在加载它的页面源下)
  4. <iframe src="..."> 内嵌框架 (内容可加载,但父页面 JS 访问其内容受同源限制)

为什么允许这些?因为它们是构建网页的基础资源(图片、样式、公共库),且默认情况下,加载这些资源不会直接暴露敏感数据给加载它们的页面脚本。


四、实战跨域问题:AJAX 请求场景

假设你的页面在 http://localhost:3000,想请求 http://localhost:4000/api/data

fetch('http://localhost:4000/api/data')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('出错了:', error));

错误信息:

Access to fetch at 'http://localhost:4000/api/data' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

原因:
端口不同 (3000 vs 4000) → 不同源 → 浏览器阻止了响应数据的读取。


五、合法跨域方案:CORS 机制(核心解决方案)

CORS (Cross-Origin Resource Sharing) 是 W3C 标准,是解决跨域问题的官方方案。原理是服务器声明允许哪些源访问资源

关键步骤:

  1. 浏览器发送请求: 你的脚本发起跨域请求(如 fetch)。
  2. 浏览器添加 Origin 头: 自动带上当前页面的源 (e.g., Origin: http://localhost:3000)。
  3. 服务器响应: 服务器检查 Origin。如果允许该源访问,则在响应中包含特定 HTTP 头:
    • Access-Control-Allow-Origin: http://localhost:3000 (或 * 表示允许任何源)
    • 对于复杂请求(如带自定义头或非简单方法),还需 Access-Control-Allow-Methods, Access-Control-Allow-Headers 等。
  4. 浏览器放行: 浏览器检查响应头。如果 Access-Control-Allow-Origin 包含当前源,则允许脚本访问响应数据。

图解:

deepseek_mermaid_20250802_f0a19d.png


六、其他跨域方案(了解即可)

  1. JSONP (JSON with Padding): 利用 <script> 标签不受同源策略限制的特性。只支持 GET 请求,逐渐被 CORS 取代。
  2. WebSocket: 协议本身支持跨域通信。
  3. 代理 (Proxy): 让你的服务器(同源)转发请求到目标服务器,再将结果返回给你的前端。常用在开发环境解决跨域。
  4. document.domain (降域): 仅适用于主域相同、子域不同的场景(如 a.example.comb.example.com),且需要双方页面都设置 document.domain = 'example.com'。限制多,不推荐。