跨域的应用场景
在 Web2.0 时代,许多应用需要聚合信息(比如来自google, 来自wikipedia的内容),因此来自A站点的网页,往往需要读取来自B的内容,这是受到同源策略约束的。
什么是同源政策(same-origin policy)
- 协议相同(FTP、HTTP等)
- 域名相同(包括每一级域名,
foo.com
和www.foo.com
不同) - 端口相同
以下行为受到限制(通常是跨域读操作):
- Cookie、LocalStorage、IndexDB 等存储性内容
- DOM 节点
- AJAX 请求不能发送
为什么需要同源政策
保护用户隐私信息,防止身份伪造等(读取Cookie)
跨域写操作与跨域嵌入操作一般不受到约束。具体参考MDN
非AJAX的跨站请求
document.domain 共享 DOM 与 存储
对于只有前缀(二级、三级等域名)不同的网页,可以设置 document.domian
来规避同源策略
|
|
具有相同domian
的可以互相读取Cookie:
也可以读取iframe
内的DOM节点
fragment identifier 共享 DOM
fragment identifier 指的是URL中hash符号#
后面的内容,不引起页面刷新
父窗口可以改变子窗口的fragment identifier,反之亦然
window.name
window.name最早是用来规避cookie缺点设立的(cookie过小, API复杂),window支持2MB以上大小
name是window的一个属性,无论window的内容如何改变其值不发生变化,因此利用iframe
标签页和window.name
可以实现跨域
首先要简单了解iframe
的相关知识,iframe
在网页中创建了一个内联框架,通过src
属性指向其他网站,每一个iframe
都有一个包裹他的window
,他是主窗口的子窗口
由此,跨站方案就非常简单了,我们首先声明一个iframe
标签指向跨站的网站,在跨站的页面内设置window.name
,当检测到onload
(只运行一次)以后把src设置回同域站点然后读取
|
|
这一解决方法的缺点主要在于需要使用iframe
并且监听子窗口,影响了网页的性能
window.postMessage
HTML5中引入, 用于跨域的父子窗口通信,不受同源策略限制.
通过postMessage
API可以实现对存储的读写,DOM的操作等
|
|
AJAX跨域
WebSocket
WebSocket通信协议不实行同源政策
JSONP
JSONP优点是兼容性好,缺点是仅支持get方法具有局限性
其设计思路是因为浏览器不对 <script>
标签进行限制,因此可以利用这一点来进行跨域请求。
- 声明一个回调函数,其参数为要获取(服务器提供的data),对参数进行操作(比如渲染进DOM)
- create一个
<script>
标签动态加入DOM tree,在src
的URL中向服务器传递该函数名 - 服务器返回一个js脚本文件,将数据包括在url中给的回调函数里,运行回调函数
CORS
CORS要求浏览器(>IE10)和服务器的同时支持,是跨域的根本解决方法,由浏览器自动完成
优点在于功能更加强大支持各种HTTP Method,缺点是兼容性不如JSONP
简单请求
定义
满足以下全部2个条件的就是简单请求,否则是非简单请求
- 使用以下三个方法之一: GET、POST、HEAD
- HTTP头不超过以下几个字段
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
流程
浏览器将在请求头加入origin
字段指定源,如果服务器支持并且该源在白名单里,将返回一个包含特殊头字段的响应,否则不包含这些特殊头字段,XMLHttpRequest
可以捕获错误,但是响应状态代码依然会是200
多出的字段:
- Access-Control-Allow-Origin(必须): 值为 * 或者请求的
origin
- Access-Control-Allow-Credentials(可选): 值只能为true表明发送cookie,默认不发送cookie不包含该字段,需要设置
xhr.withCredentials = true;
- Access-Control-Expose-Headers(可选):
XMLHttpRequest.getResponseHeader()
方法只可以获取Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma
六个基本字段和该头字段指定的字段名的值
非简单请求
预检请求(Preflight)
对于非简单请求(非基本Method
和Header
,需要首先发送一个OPTIONS请求询问服务器是否支持
浏览器根据AJAX的请求Method和Header,自动加入字段:
- Origin: 跨域必须指定的Origin
- Access-Control-Request-Method: 指定了请求要用到的方法,比如PUT
- Access-Control-Request-Headers(可选): 一个逗号分隔的字符串,指定了可能的额外Header字段
服务器检查自身是否支持后,进行回应:
- 否定预见请求: 返回没有任何CORS字段(Access-Control)的正常响应,可以用XMLHTTPRequest对象的
onerror
捕捉处理 - 肯定预见请求:
1. Access-Control-Allow-Methods: GET, POST, PUT (回复支持的方法) 2. Access-Control-Allow-Headers(如果请求具有): 逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。 3. Access-Control-Allow-Credentials: 是否传递cookie,同简单请求 4. Access-Control-Max-Age(可选): 表面预检的有效期,有效期内不发送预检请求
正常请求
在预检请求之后,正常请求与简单请求类似,请求具有Origin
段,回复具有Access-Control-Allow-Origin
等字段。