经常被提及的跨域是一种狭义上的跨域,指的是由浏览器的同源策略所限制的一种场景。同源策略(same origin policy),指的是域名、端口号、协议三者相同,他是浏览器最基本的安全策略,可以防范浏览器的CSRF、XSS攻击。
跨域的场景分析
URL 说明 是否允许通信
http://www.juejin.com/a.js
http://www.juejin.com/b.js 相同域名,不同文件/路径 允许
http://www.juejin.com/c/c.js
http://www.juejin.com:8080/a.js 相同域名,不同端口号 不允许
http://www.juejin.com/a.js
http://www.juejin.com/a.js 相同域名,不同协议 不允许
https://wwww.juejin.com/a.js
http://www.juejin.com 相同协议,不同域名 不允许
http://www.juejin1.com
http://wwww.juejin.com
http://a.juejin.com 主域名相同,子域名不相同 不允许
http://a.b.juejin.com
处理跨域的方式
1.JSONP
通过动态创建script,再请求一个带参网址实现跨域通信。JSONP只能实现get。
过程:创建一个script标签、前端生成一个全局函数、服务端返回前端全局函数的调用
原生
//创建一个script标签
var script = document.createlement('script');
script.type = 'text/javascript';
//前端生成一个全局函数,传参回调函数的名字给后端,方便后端执行时返回这个在前端定义的函数
script.src = ''http://www.juejin.com/login?user=admin&callback=handleCallBack;
function handleCallBack(res){
alert(JSON.stringfy(res))
}
服务端返回:
handleCallBack({"status":true,"user":"admin"})
vue.js
this.$http.jsonp('http://www.juejin.com/login',{
params:{};
jsonp:'handCallBack'
}).then((res)=>{
console.log(res)
})
2.document.domamin + iframe
用来解决主域名相同,子域名不同的跨域问题。
实现原理:两个页面都设置document.domain为基础主域。
1)第一个窗口:www.juejin.com/a.js
<iframe id='iframe' scr='http://2.juejin.com/a.js' style='display:none'></iframe>
<script>
document.domain = 'domain.com';
var user = admin;
</script>
2)第二个窗口:2.juejin.com/a.js
document.domain = 'domain.com'
console.log(window.parent.user)//获取父窗口中的变量值。
3.location.hash+iframe
a与b不同域,但是想要相互通信,则通过一个中间页面c来实现,三个页面之间相同域的直接用js进行访问通信,不同域之间通过location.hash传值。
1)第一个窗口:www.juejin1.com/a.html
<iframe id='iframe' src='http://www.juejin2.com/b.html' style='display:none'></iframe>
<script>
var iframe = document.getElementById('iframe');
//给第二个不同域的页面传哈希值
setTimeout(function(){
iframe.src = iframe.src+'#user=admin'
},1000);
//给第三个同域的页面开放回调
function callBackFn(res){
alert('data from third-----'+res);
}
</script>
2)第二个窗口:www.juejin2.com/b.html
<iframe id='iframe' src='http://www.juejin1.com/c.html' ></iframe>
<script>
var iframe =document.getElementById('iframe')
//监听第一个页面传过来的hash值然后传递给第三个页面
window.onHashChange = function(){
iframe.src = iframe.src + loction.hash;
}
</script>
3)第三个窗口:wwww.juejin1.com/c.html
<script>
//监听第二个窗口传过来的hash值
window.onHashChange = function(){
//通过操作同域名下的a.html的js回调,并将值传回。
window.parent.parent.callBackFn('-----'+loction.hash.replace('#user',''))
}
</script>
4.跨域资源共享(CORS)
不带cookie请求的时候,只需要后端设置Access-Control-Allow-Origin,带cookie请求的时候,需要前后端都进行设置。需要注意的是,由于同源策略的限制,获取的cookie为跨域请求接口所在域的cookie,而不是当前页的cookie。
前端设置是否带cookie:
vue.js
vue-resource设置: Vue.http.options.credentials = true
axios设置:axios.defaults.withCredentials = true
5.window.name+iframe
name值在不同的页面哪怕是不同的域名加载后都依然存在,并且可支持的name值长达2MB。
6.postMessage
window.postMessage()方法可以安全的实现跨源通信,它可以应用于:
1)页面和其打开的窗口的数据传递
2)多窗口之间消息传递
3)页面与嵌套的iframe消息传递
4)上面三个场景的跨域数据传递
使用方法:
postMessage(data,origin)
data:html5接受任意可复制的对象或者任意基本数据类型,但是有的浏览器只接受字符串,所以使用的时候最好先用JSON.stringfy()列化。
origin:协议+主机+端口号。也可以设置为 * 表示任意窗口,如果要指定和当前窗口同源的话设置为 /
<iframe id='iframe' src='http://www.juejin2.com' style='display:none'></iframe>
<script>
var iframe = document.getElementById('iframe');
iframe.onload = function(){
var data = {
user :'admin'
};
//向juejin2传递跨域数据
iframe.contentWindow.postMessage(JSON.stringfy(data),'http://www.juejin2.com')
}
//接受juejin2返回的数据
window.addEventListener('message',function(e){
consloe.log(e.data)
},false)
</script>
<script>
//接受juejin1的数据
window.addEventListener('message',function(e){
console.log(e.data);
var data = JSON.parse(e.data);
if(data){
data.number =1;
//对数据进行处理,处理之后再发给juejin1
window.parent.postMessage(JSON.stringfy('data'),'http://www.juejin1.com')
}
},false)
</script>