编码之旅
用键盘述说着工作和生活中的点点滴滴

浏览器的同源策略

Published on
/6 分钟读/---

什么是同源策略?

同源策略是浏览器自带的安全机制,目的:防止一个网站随便读取 / 操作另一个网站的数据。

它限制:从源 A 加载的脚本,不能默认访问源 B 的资源。

什么叫「同源」?

协议 + 域名 + 端口 三者完全一样,才叫同源。

只要有一个不一样,就是跨域

示例:当前页面:http://www.example.com:8080/a.html

对比 URL是否同源原因
http://www.example.com:8080/b.html✅ 同源完全一样
https://www.example.com:8080/a.html❌ 跨域协议不同(http vs https)
http://api.example.com:8080/a.html❌ 跨域域名不同
http://www.example.com:9090/a.html❌ 跨域端口不同
http://www.example.com/a.html❌ 跨域端口不同(8080 vs 默认 80)

同源策略到底限制什么?

浏览器只限制 JS 行为,不限制资源加载。

1. 被限制的行为

  • 无法通过 AJAX / fetch 读取跨域接口的响应
  • 无法读取另一个域的 Cookie、LocalStorage、SessionStorage
  • 无法获取另一个域的 DOM 内容
  • 无法获取另一个域的 IndexedDB 数据

2. 不被限制的行为

这些可以跨域加载,因为只是资源引用,不涉及数据窃取:

  • <img src="跨域图片">
  • <link href="跨域CSS">
  • <script src="跨域JS">

为什么要有同源策略?

一句话:防止你的账号被盗、数据被偷、操作被伪造。

举个真实危险场景:

  1. 你登录了 bank.com(银行),Cookie 存在浏览器
  2. 你打开了恶意网站 evil.com
  3. 如果没有同源策略:
  • evil.com 的 JS 可以直接读取 bank.com 的 Cookie
  • 可以直接调用 bank.com 的转账接口
  • 直接把你的钱转走

同源策略就是堵死这种可能

同源策略 vs CORS 关系(你最关心的)

同源策略:

是规则,是限制本身。

默认不让你跨域。

CORS(跨域资源共享):

是官方允许的 “合法通行证”。

服务器通过返回响应头:Access-Control-Allow-Origin: https://你的前端域名

告诉浏览器:这个跨域请求是我允许的,放行。

浏览器处理跨域请求流程

发起请求
   ↓
判断:是不是跨域请求?
   ├─── 否(同源)→ 直接放行,无任何限制
   ↓
是(跨域)
   ↓
判断:是不是 简单请求?
   ├─── 是 → 走【事后校验】
   └─── 否 → 走【事前校验(预检 OPTIONS)】

简单请求 → 事后校验(先发请求,后检查)

1. 什么是简单请求?

同时满足 3 条:

  1. 方法只能是:GET / HEAD / POST
  2. 请求头只有这些:
  • Accept
  • Accept-Language
  • Content-Language
  1. Content-Type 只能是:
  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain

2. 简单请求执行流程

浏览器直接发送真实请求
   ↓
服务器执行并返回响应
   ↓
浏览器检查响应头:
有无 Access-Control-Allow-Origin
   ├─── 有且匹配 → 把数据给 JS
   └─── 没有/不匹配 → 拦截响应,JS 拿不到数据

关键点:

请求已经发了,服务器已经执行了,浏览器只是拦结果。

非简单请求 → 事前校验(预检 OPTIONS)

1. 什么是非简单请求?

满足任意一条就是:

  • 方法:PUT / DELETE / PATCH
  • 自定义头:Token / Authorization / X-XXX
  • Content-Type: application/json

2. 非简单请求执行流程

浏览器先发 OPTIONS 预检请求
   ↓
服务器检查:
允许的方法、允许的头、允许的源
   ├─── 预检不通过 → 真实请求不发送
   ↓
预检通过
   ↓
浏览器缓存预检结果(Access-Control-Max-Age)
   ↓
发送真实请求
   ↓
正常返回数据

关键点:

预检不过 → 真实请求永远不发,服务器完全不执行。

为什么要分 “事前 / 事后”?

简单请求(事后)

为了兼容历史老服务器不认识 OPTIONS,不能乱加预检。

非简单请求(事前)

为了安全防止跨域直接改数据、删数据。

CORS 必须懂的 4 个头

  1. Access-Control-Allow-Origin 允许哪个源(必填)
  2. Access-Control-Allow-Methods 允许哪些方法(预检用)
  3. Access-Control-Allow-Headers 允许哪些头(预检用)
  4. Access-Control-Max-Age 预检缓存时间(秒)
下一篇 →离线语音合成