浏览器安全基石:同源策略 (Same-Origin Policy)
1. 核心概念:什么是同源策略?
同源策略(Same-Origin Policy)是浏览器提供的一个核心安全功能。我们常说的跨域问题,其根源就是浏览器的同源策略。
1.1. 适用范围
仅存在于浏览器环境。桌面应用(如 QQ)或移动端原生 APP 不存在跨域问题,因为它们不遵循浏览器的同源策略。
1.2. 目的
如果一个页面的源与它在运行过程中加载的资源的源不一致(即跨域),出于安全考虑,浏览器会对这些跨域资源进行一定的限制。
2. “源”的定义 (Origin)
要理解同源,首先要理解什么是“源”。一个源(Origin)由以下三个部分组合而成:
- 协议 (Protocol):如
http://,https:// - 主机 (Host):如
www.example.com,127.0.0.1,localhost - 端口 (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.html | https | 相同 | 相同(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)* |
*尽管
localhost和127.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 代码(如使用 XMLHttpRequest 或 fetch)尝试向一个不同源的地址发起请求时:
- 请求可以正常发出。
- 服务器可以正常接收请求并返回响应。
- 浏览器接收到响应后,会检查该响应是否符合同源策略。如果发现是跨域请求且服务器未明确许可,浏览器会拦截该响应,不允许 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.com 对 http://api.taobao.com/ 的访问被阻止了。
4.2. 代码演示
以下代码在任意网站(如百度)的控制台中执行,会尝试向淘宝发起一个跨域请求,并会复现上述错误。
// 假设在 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,但实际开发中我们确实需要这种能力。解决方案的核心思想是:让服务器告知浏览器,虽然这个请求是跨域的,但是是安全和被允许的。
主要有以下三种主流解决方案:
代理 (Proxy)
- 原理:在同源的后端服务器中设置一个代理接口,前端请求该同源接口,由后端服务器去请求真实的跨域 API,再将结果返回给前端。因为服务器之间通信不受同源策略限制。
- 场景:开发环境中最常用,例如通过
Vue CLI或Create React App的devServer.proxy配置。
CORS (Cross-Origin Resource Sharing)
- 原理:跨域资源共享。这是目前最主流、最规范的解决方案。它需要后端服务器在响应头中添加特定的字段(如
Access-Control-Allow-Origin)来明确声明允许哪些源进行跨域访问。 - 场景:生产环境中最常用的标准方案。
- 原理:跨域资源共享。这是目前最主流、最规范的解决方案。它需要后端服务器在响应头中添加特定的字段(如
JSONP (JSON with Padding)
- 原理:利用
<script>标签没有跨域限制的“漏洞”来实现。服务器不返回 JSON 数据,而是返回一段调用指定函数的 JavaScript 代码。 - 场景:一种比较古老的解决方案,存在安全风险且只支持 GET 请求,现已基本被 CORS 取代,但在面试中仍可能被问到。
- 原理:利用
6. 面试复习要点
当面试官问:“请聊一聊浏览器的同源策略”时,可以按照以下思路组织回答:
- 定义“源”:首先解释什么是“源”(协议、主机、端口)。
- 解释同源策略:说明同源策略是浏览器的安全机制,当页面源和加载资源源不一致时会触发。
- 区分限制:指出该策略对不同资源(如
script、img)的限制是宽松的,但对 Ajax 请求是极其严格的。 - 详述 Ajax 跨域:描述 Ajax 跨域的完整流程(请求发出 -> 服务器响应 -> 浏览器拦截),并说明这是我们通常所说的“跨域问题”。
- 列出解决方案:最后,给出解决 Ajax 跨域问题的三种主流方案:代理(Proxy)、CORS 和 JSONP,并可以简要说明各自的特点和适用场景。