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
浙公网安备 33010602011771号