JS跨域问题
-
协议不同
如http, https; - 端口不同
- 主域相同,子域不同
- 主域不同
-
ip地址和域名
浏览器不会自动做ip域名的映射
造成跨域的两种策略
浏览器的同源策略会导致跨域,这里同源策略又分为以下两种
DOM同源策略
禁止对不同源页面DOM进行操作。这里主要场景是iframe跨域的情况,不同域名的iframe是限制互相访问的。
XmlHttpRequest同源策略
禁止使用XHR对象向不同源的服务器地址发起HTTP请求。只要协议、域名、端口有任何一个不同,都被当作是不同的域,之间的请求就是跨域操作。
为什么要有跨域限制
了解完跨域之后,想必大家都会有这么一个思考,为什么要有跨域的限制,浏览器这么做是出于何种原因呢。其实仔细想一想就会明白,跨域限制主要是为了安全考虑。
AJAX同源策略主要用来防止CSRF攻击。如果没有AJAX同源策略,相当危险,我们发起的每一次HTTP请求都会带上请求地址对应的cookie,那么可以做如下攻击:用户登录了自己的银行页面 http://mybank.com,http://mybank.com向用户的cookie中添加用户标识。用户浏览了恶意页面 http://evil.com。执行了页面中的恶意AJAX请求代码。http://evil.com向http://mybank.com发起AJAX HTTP请求,请求会默认把http://mybank.com对应cookie也同时发送过去。银行页面从发送的cookie中提取用户标识,验证用户无误,response中返回请求数据。此时数据就泄露了。而且由于Ajax在后台执行,用户无法感知这一过程。
DOM同源策略也一样,如果iframe之间可以跨域访问,可以这样攻击:做一个假网站,里面用iframe嵌套一个银行网站 http://mybank.com。把iframe宽高啥的调整到页面全部,这样用户进来除了域名,别的部分和银行的网站没有任何差别。这时如果用户输入账号密码,我们的主网站可以跨域访问到http://mybank.com的dom节点,就可以拿到用户的输入了,那么就完成了一次攻击。
跨域的解决方式
解决方案
document.domain
window.name
jsonp
postMessage
cors
document.domain
-
关键点
跨域分为两种,一种xhr不能访问不同源的文档,另一种是不同window之间不能进行交互操作;
document.domain主要是解决第二种情况,且只能适用于主域相同子域不同的情况;
document.domain的设置是有限制的,我们只能把document.domain设置成自身或更高一级的父域,且主域必须相同。例如:a.b.example.com中某个文档的document.domain可以设成a.b.example.com、b.example.com 、example.com中的任意一个,但是不可以设成c.a.b.example.com,因为这是当前域的子域,也不可以设成baidu.com,因为主域已经不相同了。 - 兼容性:所有浏览器都支持;
-
优点:
可以实现不同window之间的相互访问和操作; -
缺点:
只适用于父子window之间的通信,不能用于xhr;
只能在主域相同且子域不同的情况下使用; -
使用方式
a(当前页面或父页面)页面中加入document.domain = ‘example.com’;
b(当前页面或子页面)页面中加入document.domain = ‘example.com’;
a页面访问b页面里面的数据或者方法; -
Example
有一个页面,它的地址是http://www.example.com/a.html , 在这个页面里面有一个iframe,它的src是http://example.com/b.html
1.在页面 http://www.example.com/a.html 中设置document.domain:
<iframe id = "iframe" src="http://example.com/b.html" onload = "test()"></iframe>
<script type="text/javascript">
document.domain = 'example.com';//设置成主域
function test(){
alert(document.getElementById('iframe').contentWindow);//contentWindow 可取得子窗口的 window 对象
}
</script>
2.在页面 http://example.com/b.html 中也设置document.domain:
<script type="text/javascript">
document.domain = 'example.com';//在iframe载入这个页面也设置document.domain,使之与主页面的document.domain相同
</script>
window.name
-
关键点
-
window.name在页面的生命周期里共享一个window.name;
-
兼容性
-
所有浏览器都支持;
-
优点
最简单的利用了浏览器的特性来做到不同域之间的数据传递;
不需要前端和后端的特殊配制; -
缺点
大小限制:window.name最大size是2M左右,不同浏览器中会有不同约定;
安全性:当前页面所有window都可以修改,很不安全;
数据类型:传递数据只能限于字符串,如果是对象或者其他会自动被转化为字符串,如下;
window.name非字符串测试
使用方式:修改window.name的值即可; -
Example
a.html中代码:
<script>
window.name = '我是页面a中设置的值';
setInterval(function(){
window.location = 'b.html';
},2000)//两秒后把一个新页面b.html载入到当前的window中
</script>
b.html中的代码
<script>
console.log(window.name);//读取window.name的值
</script>
jsonp
-
关键点
浏览器对XHR做了同源策略,但并没有将这种方式延续到script上(其实还有iframe,img等)
从而可以利用动态script标签技术来做到跨域请求的作用。至于为什么会这样设计,本人也不太清楚,有可能是历史遗迹(漏洞),有可能是某些方面的技术瓶颈,也有可能是为了满足某些需求专门定制的,总之这项技术方案我们过去可以用,现在可以用就ok,至于将来应该也是会存在的,毕竟现在已经应用在很多家站点上,就算会废弃,也会有一段时间迭代。
在js中,我们直接用XMLHttpRequest请求不同域上的数据时,是不可以的。但是,在页面上引入不同域上的js脚本文件却是可以的,jsonp正是利用这个特性来实现的。 -
兼容性
所有浏览器都兼容这种方式; -
优点
很明显前端可以很轻松的做到跨域请求; -
缺点
只能通过GET方式请求,一方面是参数长度有限制,二是安全性比较差;
后端需要知道前端的cb是什么样的结构,主要在参数和回调名;
后端需要进行参数和cb的拼接然后才能执行;
<script type="text/javascript">
function dosomething(jsondata){
//处理获得的json数据
}
</script>
<script src="http://example.com/data.php?callback=dosomething"></script>
js文件载入成功后会执行我们在url参数中指定的函数,并且会把我们需要的json数据作为参数传入。所以jsonp是需要服务器端的页面进行相应的配合的。
<?php
$callback = $_GET['callback'];//得到回调函数名
$data = array('a','b','c');//要返回的数据
echo $callback.'('.json_encode($data).')';//输出
?>
最终,输出结果为:dosomething(['a','b','c']);
postMessage
-
关键点
window.postMessage(message,targetOrigin) 方法是html5新引进的特性,可以使用它来向其它的window对象发送消息,无论这个window对象是属于同源或不同源,目前IE8+、FireFox、Chrome、Opera等浏览器都已经支持window.postMessage方法。 -
兼容性
移动端可以放心用,但是pc端需要做降级处理,具体可以根据文中介绍的这几种跨域方式来则情选择; -
优点
不需要后端介入就可以非常简单的的做到跨域,一个函数外加两个参数(请求url,发送数据)就可以搞定;
移动端兼容性好; -
缺点
无法做到一对一的传递方式:监听中需要做很多消息的识别,由于postMessage发出的消息对于同一个页面的不同功能相当于一个广播的过程,该页面的所有onmessage都会收到,所以需要做消息的判断;
安全性问题:三方可以通过截获,注入html或者脚本的形式监听到消息,从而能够做到篡改的效果,所以在postMessage和onmessage中一定要做好这方面的限制;
发送的数据会通过结构化克隆算法进行序列化,所以只有满足该算法要求的参数才能够被解析,否则会报错,如function就不能当作参数进行传递; -
例子
otherWindow.postMessage(message, targetOrigin);
otherWindow: 对接收信息页面的window的引用。可以是页面中iframe的contentWindow属性;window.open的返回值;通过name或下标从window.frames取到的值。
message: 所要发送的数据,string类型。
targetOrigin: 用于限制otherWindow,“*”表示不作限制 -
Example
a.com/index.html中的代码:
<iframe id="ifr" src="b.com/index.html"></iframe>
<script type="text/javascript">
window.onload = function() {
var ifr = document.getElementById('ifr');
// 若写成'http://b.com/c/proxy.html'效果一样
// 若写成'http://c.com'就不会执行postMessage了
var targetOrigin = 'http://b.com';
ifr.contentWindow.postMessage('I was there!', targetOrigin);
};
</script>
b.com/index.html中的代码:
<script type="text/javascript">
window.addEventListener('message', function(event){
// 通过origin属性判断消息来源地址
if (event.origin == 'http://a.com') {
alert(event.data); // 弹出"I was there!"
alert(event.source); // 对a.com、index.html中window对象的引用
// 但由于同源策略,这里event.source不可以访问window对象
}
}, false);
</script>
cors(Cross-origin resource sharing)
- 关键点
- cors是一种通过前后端http header配置来进行跨域的一种方式;
兼容性:如果不考虑pc端的IE,移动端的opera的话那兼容性还是不错的,针对ie和opera可以做适当的降级处理;
poseMessage兼容性
安全策略
请求
origin:通过http头中的origin判断域名是否是允许的;
Example-Same-origin:如果http origin不存在,最好能够自己在请求头中加入该参数来标示是否是同源,true表示请求来自于同域名下(同域名下请求不带origin);如果该字段存在并且为true则允许请求接口,否则禁止;
Example_source_origin:该参数同origin,是在origin不存在的情况下用来标示请求来源的url;
返回
Access-Control-Allow-Origin: origin,origin表示允许哪些网站请求,不建议设置为*;
Access-Control-Expose-Headers:Example-Access-Control-Allow-Source-Origin,允许http返回中包含该字段,可以通过这种方式在返回头中加入自定义字段,如该例子中的Example-Access-Control-Allow-Source-Origin; -
优点
前端方便不少,只需要发请求而不用考虑跨域问题;
安全性能够得以控制和保障; -
缺点
兼容性不全面,需要做降级处理; -
Example
对于客户端,我们还是正常使用xhr对象发送ajax请求。唯一需要注意的是,我们需要设置我们的xhr属性withCredentials为true,不然的话,cookie是带不过去的哦,设置: xhr.withCredentials = true;对于服务器端,需要在 response header中设置如下两个字段:
Access-Control-Allow-Origin: http://www.yourhost.com
Access-Control-Allow-Credentials:true
这样,我们就可以跨域请求接口了。
网友评论