什么是源(域)
在W3C的定义中,源是由协议、主机(IP 地址或者域名)和端口三者确定。如果两个 URL 的协议、主机(IP 地址或者域名)和端口都一样的话,那么这两个 URL 就是同源的。
同源策略
由于源与源之间是未知且默认的,所以非本源中的资源(即URL对应的资源)是不可控的。在早期的互联网,不同源的内容可以随意访问。因此有人就借此来攻击网站。如,在JavaScript脚本中添加恶意代码,然后让目标网站访问加载,从而实现攻击目标网站(即,XSS攻击)。此外还有CSRF(Cross-Site Request Forgery,跨站请求伪造)网络攻击的方式。由此可见,限制不同源之间的资源访问非常重要。
因此,出于安全的考虑,最早由Netscape公司于1995年在自家浏览器中引入同源策略,对不同源之间的资源交互进行了限制。后续各家浏览器厂商跟进引入。目前,所有的浏览器都在实行这个政策。
需要注意的是,并不是所有的不同源的资源交互操作都是禁止的。诸如,script 标签、img 标签等是允许跨域访问。
不同源也称为跨域。一般情况下,两者是同一个意思。
如何实现跨域资源共享
虽然同源策略保证了网站的安全,但很多时候又不得不访问不同源的资源。其实,在早期的时候,人们巧妙的借助script 标签、img 标签可以加载不同域资源的特性来实现跨域资源访问。即,JSONP (2005年)和 图片Ping。但这两种技术都存在一定的限制以及安全隐患,现在不是很推荐使用。后来,W3C在2014年1月16日正式发布CORS(Cross-Origin Resource Sharing,跨域资源共享)标准方案,定义了在必须访问跨域资源时,浏览器与服务器应该如何沟通。使得跨域资源共享成为了一项安全、可控、可靠的技术。目前主流的浏览器已全部支持。
现在,最常用的跨域资源共享的技术是 CORS;JSONP 更多情况下是 CORS 的一种替代方案(例如,不支持 CORS 的老浏览器);而 图片 Ping 技术基本很少见了。
跨域资源共享(CORS)
CORS(Cross-Origin Resource Sharing,跨域资源共享)是一个系统,它由一系列传输的 HTTP header组成,这些 HTTP 标头决定浏览器是否阻止前端 JavaScript 代码获取跨域请求的响应。借助 CORS,web 服务器可以选择哪些跨域请求允许访问到它们的资源。
CORS 工作过程
CORS 很简单,浏览器将 CORS 需要的相关信息通过请求头发送给服务器,服务器根据请求过来的信息判断是否允许访问,并把结果回传给浏览器。浏览器对接收的到信息进一步做处理,对于被拒绝的请求给出 CORS 错误。
- 首先浏览器依照 CORS 的规定,根据请求接口的复杂度区分为简单请求和复杂请求。
- 简单请求:将在请求头中附带
Origin
发送给 web 服务器 - 复杂请求:将根据请求的基本信息(包括
Origin
、Access-Control-Request-Method
等)生成一个预检请求(Preflight request),向 web 服务器询问是否支持 CORS 。
- 简单请求:将在请求头中附带
- 当 web 服务器接收到请求时, 针对不同请求有如下处理:
- 简单请求:根据请求头中的
Origin
来决定是否拒绝响应。- 如果
Origin
指定的源,不在许可范围内,服务器会返回一个正常的 HTTP 响应。浏览器发现,这个响应的头信息没有包含Access-Control-Allow-Origin
字段就知道出错了,从而拦截响应数据并抛出 CORS 错误,被XMLHttpRequest
的onerror
回调函数捕获; - 如果服务器认为这个请求可以接受,则在响应头中的
Access-Control-Allow-Origin
中会发接受的源信息。
- 如果
- 预检请求:根据请求头中的
Origin
、Access-Control-Request-Method
以及Access-Control-Request-Headers
来判断是否允许这种类型的请求。- 如果服务器确认允许这一类的请求,则响应正常,并在响应头中携带
Access-Control-Allow-Origin
、Access-Control-Allow-Methods
、Access-Control-Allow-Headers
以及Access-Control-Max-Age
等字段。然后,浏览器发送真实请求。这时发送的真实请求的处理逻辑和简单请求一致。 - 如果服务器否认这一类的请求,同样会返回一个正常的 HTTP 响应。但是没有任何 CORS 相关的头信息字段。这时,浏览器就会认定,服务器不同意预检请求,因此拦截响应数据触发一个错误,被
XMLHttpRequest
对象的onerror
回调函数捕获。
- 如果服务器确认允许这一类的请求,则响应正常,并在响应头中携带
- 简单请求:根据请求头中的
关于浏览器拦截跨域请求的响应内容:
当一个请求被浏览器识别为不允许的跨域请求时,浏览器会自动拦截请求的响应内容,阻止响应内容加载到渲染进程中。这也就是为什么在 Devtools 的 Network 中查看被阻止的跨域请求时,Response 是一片空白的原因。
其中,针对跨域标签(如:
<script>
,<img>
)请求的响应内容,浏览器主要采用 CORB(Cross-Origin Read Blocking)技术来进行拦截。CORB 是一种判断是否要在跨站资源数据到达页面之前阻断其到达当前站点进程中的算法,降低了敏感数据暴露的风险。了解即可,详见:Cross-Origin Read Blocking (CORB) - 掘金 (juejin.cn)
借助CORS,Web 服务器就可以精确的控制跨域请求,只对可信的域的请求开放。