同源策略
浏览器为了保证用户信息的安全,防止恶意网站窃取数据,禁止不同域之间的JS进行交互。
对于浏览器而言,只要域名、协议、端口中有一个不同就会引发同源策略。
同源策略限制以下行为:
1.Cookie、LocalStorage 和 IndexDB 无法读取
2.DOM 和 JS 对象无法获取
3.Ajax请求无法发送
跨域场景
跨域解决方案
1. JSONP
为了减轻web服务器的负载,JS、CSS、img等静态资源会被放在CDN服务器上,不同源也会被浏览器允许加载。
JSONP就是利用了这个原理,动态创建一个script标签,通过src引入一个JS脚本文件,这个文件的内容是一个函数调用,参数是服务器返回的数据。需在页面定义好回调函数来处理返回的数据。
不足之处:
只能使用GET方法,不能使用POST方法。
不能进行JSONP调用的错误处理。
2.CORS
CORS的全称是跨域资源共享。它是一个W3C标准,允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
使用方式:
在服务端设置Access-Control-Allow-Origin响应头。
(1)简单请求
请求方式属于HEAD GET POST
HTTP头信息不超出
Accept、Accept-Language、Content-Language、Last-Event-ID、Content-Type
发现是简单请求,浏览器在头信息中增加一个origin字段,用来说明本次请求来自哪个源。服务器根据这个值决定是否同意。
(2)非简单请求
复杂请求的请求方式是PUT或DELETE
复杂请求会在通信前加一次预检请求,使用OPTIONS方法表示该请求用于询问,询问服务器当前域名是否在许可名单中,得到肯定后才会发出正式的http请求。
优点:
CORS支持所有类型的请求。
开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理。
缺点:
兼容性差,IE10以上才支持。
3.document.domain+iframe
两个页面都通过js强制设置document.domain为基础主域,就实现了同域。这种方法仅限主域相同的情况。
使用方法:
比如在 www.domain.com/a.html 的页面里要访问 www.domain.com/b.html 里面的东西。
父窗口:(www.domain.com/a.html)
<iframe id="iframe" src="http://child.domain.com/b.html"></iframe>
<script>
document.domain = 'domain.com';
var user = 'admin';
</script>
子窗口:(www.domain.com/b.html)
<script>
document.domain = 'domain.com';
// 获取父窗口中变量
alert('get js data from parent ---> ' + window.parent.user);
</script>
4.location.hash+iframe
a欲与b跨域相互通信,通过中间页c来实现。 三个页面,不同域之间利用iframe的location.hash传值,相同域之间直接js访问来通信。
5.window.name+iframe
name值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值。
浏览器跨域iframe禁止互相调用或传值。但是调用iframe时,window.name却不变,正是利用这个特性来互相传值,当然跨域下是不允许读取iframe的window.name值。所以我们还需要加一个空的代理页面(与主页面同域)
使用方法:
(1)先准备好主页面、数据页面和代理页面。
http://www.a.com/main.html //主页面
http://www.a.com/other.html // 代理页面,要求和应用页面在同一个域。一般是一个空的html。
http://www.b.com/data.html //应用页面获取数据的页面,简称:数据页面
(2) 数据页面将数据传到window.name中去。
window.name=“string”; //可以是其他类型的数据,比如数组,对象等等。
(3)主页面通过代理页面读取数据。
6.postMessage()
这是H5的新特性,是一个发送消息的API。
可以解决以下问题:
(1)页面和其打开的新窗口的数据传递
(2)多窗口之间消息传递
(3)页面与嵌套的iframe消息传递
(4)上面三个场景的跨域数据传递
使用方法:
postMessage(data,origin)方法接受两个参数
data: html5规范支持任意基本类型或可复制的对象,但部分浏览器只支持字符串,所以传参时最好用JSON.stringify()序列化。
origin: 协议+主机+端口号,也可以设置为"*",表示可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"。
主页面:
<iframe id="iframe" src="http://www.domain2.com/b.html" style="display:none;"></iframe>
<script>
var iframe = document.getElementById('iframe');
iframe.onload = function() {
var data = {
name: 'aym'
};
// 向domain2传送跨域数据
iframe.contentWindow.postMessage(JSON.stringify(data), 'http://www.domain2.com');
};
// 接受domain2返回数据
window.addEventListener('message', function(e) {
alert('data from domain2 ---> ' + e.data);
}, false);
</script>
数据页面:
<script>
// 接收domain1的数据
window.addEventListener('message', function(e) {
alert('data from domain1 ---> ' + e.data);
var data = JSON.parse(e.data);
if (data) {
data.number = 16;
// 处理后再发回domain1
window.parent.postMessage(JSON.stringify(data), 'http://www.domain1.com');
}
}, false);
</script>
7.nginx代理
同源策略是浏览器的安全策略,不是HTTP协议的一部分。服务器端调用HTTP接口只是使用HTTP协议,不会执行JS脚本,不需要同源策略,也就不存在跨越问题。
通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。
8.websocket
WebSocket是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯。