Skip to content

浏览器安全基石:同源策略 (Same-Origin Policy)

1. 核心概念:什么是同源策略?

同源策略(Same-Origin Policy)是浏览器提供的一个核心安全功能。我们常说的跨域问题,其根源就是浏览器的同源策略。

1.1. 适用范围

仅存在于浏览器环境。桌面应用(如 QQ)或移动端原生 APP 不存在跨域问题,因为它们不遵循浏览器的同源策略。

1.2. 目的

如果一个页面的源与它在运行过程中加载的资源的源不一致(即跨域),出于安全考虑,浏览器会对这些跨域资源进行一定的限制。

2. “源”的定义 (Origin)

要理解同源,首先要理解什么是“源”。一个源(Origin)由以下三个部分组合而成:

  1. 协议 (Protocol):如 http://, https://
  2. 主机 (Host):如 www.example.com, 127.0.0.1, localhost
  3. 端口 (Port):如 :80, :443, :8080

注意:如果 URL 中未明确指定端口,浏览器会使用默认端口(HTTP 为 80,HTTPS 为 443)。

同源指的是两个 URL 的协议、主机、端口三者完全相同。只要有任意一项不同,即为不同源,也称为跨源跨域

2.1. 同源与跨域示例

假设当前页面的源为 http://www.example.com/index.html

对比 URL协议主机端口是否同源原因
http://www.example.com/page.html相同相同相同(80)路径不同不影响同源判断
https://www.example.com/index.htmlhttps相同相同(443)协议不同 (http vs https)
http://www.example.com:8080/index.html相同相同:8080端口不同 (80 vs 8080)
http://sub.example.com/index.html相同sub.example.com相同(80)主机名不同
http://127.0.0.1/index.html相同127.0.0.1相同(80)主机名不同 (www.example.com vs 127.0.0.1)*

*尽管 localhost127.0.0.1 通常指向同一台机器,但对于同源策略来说,它们是不同的主机名。

3. 同源策略的限制

3.1. 页面的源 vs. 加载资源的源

  • 页面的源:指浏览器地址栏中当前页面的 URL 所对应的源。
  • 加载资源的源:指页面在渲染和运行过程中,通过各种方式请求的外部资源的源。例如:
    • <img> 标签加载的图片
    • <link> 标签加载的 CSS
    • <script> 标签加载的 JavaScript 文件
    • Ajax 请求的 API 接口

3.2. 不同资源的限制宽松度

浏览器对不同类型的跨域资源限制程度不同:

  • 宽松限制:对于 <img>, <link>, <script> 等标签加载的跨域资源,浏览器通常允许加载和执行。尽管存在一些细微的限制,但日常开发中基本无感。
  • 严格限制:对于 Ajax 请求,同源策略的限制非常严格

3.3. 对 Ajax 的严格限制

这是我们通常所说的“跨域问题”的核心所在。当页面中的 JavaScript 代码(如使用 XMLHttpRequestfetch)尝试向一个不同源的地址发起请求时:

  1. 请求可以正常发出
  2. 服务器可以正常接收请求并返回响应
  3. 浏览器接收到响应后,会检查该响应是否符合同源策略。如果发现是跨域请求且服务器未明确许可,浏览器会拦截该响应,不允许 JavaScript 代码读取响应内容,并在控制台抛出错误。

4. 跨域问题的识别与演示

4.1. 浏览器控制台错误

当你在开发中遇到类似以下的浏览器控制台错误时,通常意味着发生了 Ajax 跨域问题:

Access to fetch at 'http://api.taobao.com/' from origin 'http://www.baidu.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

这个错误明确指出,由于 CORS 策略,从源 http://www.baidu.comhttp://api.taobao.com/ 的访问被阻止了。

4.2. 代码演示

以下代码在任意网站(如百度)的控制台中执行,会尝试向淘宝发起一个跨域请求,并会复现上述错误。

javascript
// 假设在 www.baidu.com 的页面控制台中执行
// 页面的源: https://www.baidu.com

// 目标资源的源: https://www.taobao.com (不同源)
fetch('https://www.taobao.com')
  .then(response => response.text())
  .then(data => {
    console.log(data); // ❌ 这行代码不会执行
  })
  .catch(error => {
    console.error('发生错误:', error); // ✅ 会在这里捕获到错误
  });

5. 解决跨域问题的常见方案

虽然浏览器默认禁止跨域 Ajax,但实际开发中我们确实需要这种能力。解决方案的核心思想是:让服务器告知浏览器,虽然这个请求是跨域的,但是是安全和被允许的

主要有以下三种主流解决方案:

  1. 代理 (Proxy)

    • 原理:在同源的后端服务器中设置一个代理接口,前端请求该同源接口,由后端服务器去请求真实的跨域 API,再将结果返回给前端。因为服务器之间通信不受同源策略限制。
    • 场景:开发环境中最常用,例如通过 Vue CLICreate React AppdevServer.proxy 配置。
  2. CORS (Cross-Origin Resource Sharing)

    • 原理:跨域资源共享。这是目前最主流、最规范的解决方案。它需要后端服务器在响应头中添加特定的字段(如 Access-Control-Allow-Origin)来明确声明允许哪些源进行跨域访问。
    • 场景:生产环境中最常用的标准方案。
  3. JSONP (JSON with Padding)

    • 原理:利用 <script> 标签没有跨域限制的“漏洞”来实现。服务器不返回 JSON 数据,而是返回一段调用指定函数的 JavaScript 代码。
    • 场景:一种比较古老的解决方案,存在安全风险且只支持 GET 请求,现已基本被 CORS 取代,但在面试中仍可能被问到。

6. 面试复习要点

当面试官问:“请聊一聊浏览器的同源策略”时,可以按照以下思路组织回答:

  1. 定义“源”:首先解释什么是“源”(协议、主机、端口)。
  2. 解释同源策略:说明同源策略是浏览器的安全机制,当页面源和加载资源源不一致时会触发。
  3. 区分限制:指出该策略对不同资源(如 scriptimg)的限制是宽松的,但对 Ajax 请求是极其严格的。
  4. 详述 Ajax 跨域:描述 Ajax 跨域的完整流程(请求发出 -> 服务器响应 -> 浏览器拦截),并说明这是我们通常所说的“跨域问题”。
  5. 列出解决方案:最后,给出解决 Ajax 跨域问题的三种主流方案:代理(Proxy)CORSJSONP,并可以简要说明各自的特点和适用场景。