JS系列 - 跨域方法
同源策略:
浏览器中有个安全机制,就是同源策略,下面介绍同源策略。同源是指同协议
、同域名
、同端口
,必须三同,缺一不可。
限制范围:
(1) Cookie、LocalStorage 和 IndexDB 无法读取。
(2) DOM 无法获得。
(3) AJAX 请求不能发送。
避免方案:
1. 设置docnment.domain
两个网页一级域名相同,二级域名不同,浏览器允许通过设置document.domain
共享 Cookie。
举例:
A网页是http://w1.example.com/a.html;
B网页是http://w2.example.com/b.html
,
设置相同的document.domain
,两个网页就可以共享Cookie。document.domain = 'example.com';
iframe上使用:
实现原理:两个页面都通过js强制设置document.domain为基础主域,就实现了同域。
2. location.hash的iframe跨域
实现原理:
a 与 b 跨域相互通信,通过中间页c来实现。 三个页面,不同域之间利用iframe的location.hash传值,相同域之间直接js访问来通信。具体步骤:
A --> B:
A页面,有个iframe的B页面,A页面通过选择器获取到B的dom节点,在B的src上添加哈希值;
B页面通过监听哈希值的变化,获取到A页面传递的数据;
B --> C:
B页面有个iframe的C页面,地址和A页面同源;
B页面通过选择器获取到C的dom节点,在C的src上添加哈希值;
C页面通过监听哈希值的变化,获取到B页面传递的数据;
C页面和A页面同源,所以能获取A页面的window对象(window.parent.parent
),通过传参数调用A页面的方法,间接达到B与A通讯;
3.window.name的iframe跨域
浏览器窗口有window.name
属性。这个属性的最大特点是,无论是否同源,只要在同一个窗口里,前一个网页设置了这个属性,后一个网页可以读取它。
-
父窗口先打开一个子窗口,载入一个不同源的网页,该网页将信息写入
window.name
属性。window.name = data;
-
接着,子窗口跳回一个与主窗口同域的网址。
location = 'http://parent.url.com/xxx.html';
-
然后,主窗口就可以读取子窗口的
window.name
了。var data = document.getElementById('myFrame').contentWindow.name;
这种方法的优点是,
window.name
容量很大,可以放置非常长的字符串;缺点是必须监听子窗口window.name
属性的变化,影响网页性能。
4.postMessage跨域(跨文本档、多窗口、跨域消息传递)
HTML5引入了一个全新的API:跨文档通信 API(Cross-document messaging)。这个API为window
对象新增了一个window.postMessage
方法,允许跨窗口通信,不论这两个窗口是否同源。
语法:otherWindow.postMessage(message, targetOrigin, [transfer]);
第一个参数是具体的信息内容,
第二个参数是接收消息的窗口的源(origin),即"协议 + 域名 + 端口"。也可以设为*
,表示不限制域名,向所有窗口发送。
父向子发信息:子页面window对象.postMessage(参数, 子页面url)
var popup = window.open('http://bbb.com', 'title');
popup.postMessage('Hello World!', 'http://bbb.com');
子向父发信息:父页面window对象.postMessage(参数, 父页面url)
window.opener.postMessage('Nice to see you', 'http://aaa.com');
父子窗口通过message
事件获取信息
window.addEventListener('message', function(e) {
console.log(e.data);
},false);
message
事件的事件对象event
,提供以下三个属性。
-
event.source
:发送消息的窗口event.origin
: 消息发向的网址event.data
: 消息内容
下面是iframe通过postMessage进行跨域:
5.JSONP
同源政策规定,AJAX请求只能发给同源的网址,否则就报错。除了架设服务器代理,jsonp规避这个限制。
优点:jsonp是服务器与客户端跨源通信的常用方法。简单适用,老式浏览器全部支持,服务器改造非常小。
它的基本思想是,使用<script>标签可以跨域的特点实现,具体步骤如下:
1. 网页动态插入<script>
元素,由它向跨源网址发出请求,请求的查询字符串有一个callback
参数,指定回调函数的名字。
2. 服务器返回 script 标签的内容,内容就是调用指定的回调函数,并且将返回数据以参数的显示传递;
6.CORS
CORS需要浏览器和服务器同时支持。目前所有浏览器都支持该功能,IE浏览器不能低于IE10;
浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)
整个通信过程,都是浏览器自动完成。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。
浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
简单请求:
同时满足以下两大条件,就属于简单请求
1. 请求方法是以下三种方法之一:HEAD、GET、POST
2. HTTP的头信息不超出以下几种字段:Accept、Accept-Language、Content-Language、Last-Event-ID、Content-Type:只限于三个值application/x-www-form-urlencoded
、multipart/form-data
、text/plain
基本流程:
1.浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin
字段;Origin
字段说明本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。
2.如果Origin
指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应。浏览器发现回应的头信息没有包含Access-Control-Allow-Origin
字段,抛出一个错误,被XMLHttpRequest
的onerror
回调函数捕获(注意,这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200)
3.在指定范围,服务器返回的响应,会多出几个头信息字段:
-
Access-Control-Allow-Origin: http://api.bob.com(请求时
Origin
字段的值或者*
,*表示接受任意域名的请求) -
Access-Control-Allow-Credentials: true(布尔值,表示是否允许发送Cookie;默认不发送false; 同时开发者必须在AJAX请求中打开
withCredentials
属性xhr.withCredentials = true
)
-
Access-Control-Expose-Headers: FooBar (
XMLHttpRequest
对象的getResponseHeader()
方法只能拿到6个基本字段:Cache-Control
、Content-Language
、Content-Type
、Expires
、Last-Modified
、Pragma
。如果想拿到其他字段,就必须在Access-Control-Expose-Headers
里面指定。例子指定,getResponseHeader('FooBar')
可以返回FooBar
字段的值)
非简单请求:
对服务器有特殊要求的请求,比如请求方法是PUT
或DELETE
,或者Content-Type
字段的类型是application/json;
基本流程:
1.会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求;浏览器先询问服务器,当前域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。得到肯定答复,浏览器才会发出正式的XMLHttpRequest
请求,否则就报错;("预检"请求用的请求方法是OPTIONS
)
2.
待续。。。。
参考:http://www.ruanyifeng.com/blog/2016/04/cors.html