跨域解决方案简述
总结、技术 Dec 20, 2015
什么是跨域
不同域名之间的数据互动即为跨域。例如我们常见的ajax请求、cookie、localStorage、sessionStorage等。提到跨域,自然要提到同源策略。
什么是同源策略
同源策略/SOP(Same origin policy)是一种约定,由Netscape公司1995年引入浏览器,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。所谓同源是指”协议+域名+端口”三者相同,即便两个不同的域名指向同一个ip地址,也非同源。
同源策略会限制的行为:
1.H5本地存储的数据无法读取(cookie、localStorage、sessionStorage、indexDB)
2.DOM和JS对象无法获得
3.ajax请求不能发送
存在跨域的场景
1.不同域名
http://www.domain.com
http://www.domain2.com
2.不同子域名
http://a.domain.com
http://b.domain.com
3.域名和域名对应的ip
http://www.domain.com
http://192.168.2.3
4.不同端口
http://www.domain.com:8000
http://www.domain.com:7000
5.不同协议
http://www.domain.com
https://www.domain.com
跨域的解决方案
1.jsonp
2.document.domain+iframe
3.postMessage
4.CORS(跨域资源共享)
5.nginx代理
6.WebSocket协议
jsonp解决方案
我们知道,页面中引用的css、js、img等静态资源是可以和页面域名所不相同的,而这是被浏览器所允许的。基于此原理,我们可以通过动态的创建script标签,并请求一个带参数的网址链接实现跨域通信。
前端请求:
var script = document.createElement("script");
script.type = "text/javascript";
// 传参一个回调函数名给后端
script.src = "http://www.domain2.com:8000/data?op=getData&callback=jsonp1";
document.head.appendChild(script);
// 执行回调函数
function jsonp1(res) {
console.log(res);
}
后端返回数据:
jsonp1({errorcode:0, data:{}})
由于jsonp是使用创建script标签的方式实现的,所以只能支持get请求方式,无法使用post请求方式。
document.domain+iframe解决方案
此方法仅限主域相同,子域不同的跨域场景。是通过设置两个页面的document.domain为基础主域,来解决跨域问题。
父窗口(http://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>
子窗口(http://child.domain.com/b.html)
document.domain = "domain.com";
// 获取父窗口中的变量
console.log(window.parent.user);
postMessage解决方案
postMessage是HTML5 XMLHttpRequest Level 2 中的API,且是为数不多可以跨域操作的window属性之一,它可以解决以下方面的问题:
1.页面和其打开的新窗口的数据传递
2.多窗口之间消息传递
3.页面与嵌套的iframe消息传递
4.上面三个场景的跨域数据传递
postMessage的用法:
postMessage(data, origin)
data:html5规范支持任意基本类型或可复制的对象,但部分浏览器只支持字符串,所以最好使用JSON.stringify()序列化一下。
origin:协议+主机+端口号,也可以设置为“*”,表示可以传递给任意窗口,如果要指定和当前窗口同源的话设置为“/”。
代码示例:
页面A:http://www.domain1.com/a.html
<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) {
console.log(e.data);
}, false);
</script>
页面B:http://www.domain2.com/b.html
// 接收domain1的数据
window.addEventListener('message', function(e) {
console.log(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);
CORS(跨域资源共享)
cors包括两种方式,一种是普通的跨域请求,一种是需要带cookie的跨域请求。
普通的跨域请求:只需要服务器设置head头,添加Access-Control-Allow-Origin即可。
Access-Control-Allow-Origin: "*" // 允许所有域名跨域请求
Access-Control-Allow-Origin: "http://www.domain.com" // 只允许域名为www.domain.com的跨域请求
带cookie的跨域请求:不仅需要设置服务器返回的head头,前端也需要设置withCredentials。
需要注意的是,由于同源策略的限制,所读取的cookie为跨域请求接口所在域的cookie,而非当前页。
目前,所有浏览器均支持该功能(IE8+:IE8/9需要使用XDomainRequest对象来支持CORS),CORS也已经成为主流的跨域解决方案。
原生ajax设置:
var xhr = new XMLHttpRequest(); // IE8/9需用window.XDomainRequest兼容
// 前端设置是否带cookie
xhr.withCredentials = true;
xhr.open('post', 'http://www.domain2.com:8080/login', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('user=admin');
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
alert(xhr.responseText);
}
};
Jquery ajax:
$.ajax({
...
xhrFields: {
withCredentials: true // 前端设置是否带cookie
},
crossDomain: true, // 会让请求头中包含跨域的额外信息,但不会含cookie
...
});
Vue框架axios设置:
axios.defaults.withCredentials = true
当使用带cookie的跨域请求时,后端设置的Access-Control-Allow-Origin不能再为“*”了,否则会报错。
后端设置:
// 允许跨域访问的域名:若有端口需写全(协议+域名+端口),若没有端口末尾不用加'/'
setHeader("Access-Control-Allow-Origin", "http://www.domain1.com");
// 允许前端带认证cookie:启用此项后,上面的域名不能为'*',必须指定具体的域名,否则浏览器会提示错误
setHeader("Access-Control-Allow-Credentials", "true");
nginx反向代理方案
跨域原理: 同源策略是浏览器的安全策略,不是HTTP协议的一部分。服务器端调用HTTP接口只是使用HTTP协议,不会执行JS脚本,不需要同源策略,也就不存在跨越问题。
实现思路:通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。
WebSocket解决方案
WebSocket协议是HTML5的一种新协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是server push技术的一种很好的实现。
这里就不具体展开讲述WebSocket的使用api了。