同源策略和跨域实现
同源策略
怎样才算同源
同源是指域名(主机名或者ip地址)、端口、协议相同。不同的客户端脚本(javascript、ActionScript)在没有明确授权的情况下,不允许读取对方的资源。
下面举例,与URL”http://www.example.com/dir/page.html“进行对比说明:(转自http://www.jianshu.com/p/beb059c43a8b)
对比URL | 结果 | 分析 |
---|---|---|
http://www.example.com/dir/page2.html | 同源 | 协议相同、主机相同、端口相同 |
http://www.example.com/dir2/other.html | 同源 | 协议相同,主机相同、端口相同 |
http://username:password@www.example.com/dir2/other.html | 同源 | 协议相同、主机相同、端口相同 |
http://www.example.com:81/dir/other.html | 不同源 | 协议相同,主机相同,端口不同 |
https://www.example.com/dir/other.html | 不同源 | 协议不同 |
http://en.example.com/dir/other.html | 不同源 | 主机不同 |
http://example.com/dir/other.html | 不同源 | 主机不同(需要精确匹配) |
http://v2.www.example.com/dir/other.html | 不同源 | 主机不同 |
http://www.example.com:80/dir/other.html | 看情况 | 端口明确,依赖浏览器实现 |
2. 什么是同源策略
同domain( 或者ip),同端口,同协议视为同一个域,一个域内的脚本仅仅具有本域内的权限,可以理解为本域脚本只能读写本域内的资源,而无法访问其他域的资源,这种安全限制称为同源策略。同源策略是由Netscape提出的一个安全策略.
3. 同源策略的优缺点
同源策略是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少同源策略,浏览器很容易受到XSS、CSFR等攻击。这是同源策略的优点,它提升了web前段的安全性,它为安全上网等提供了一定的保障。但同时,也在一定程度上限制了web拓展上的灵活性。
跨域实现
什么是跨域
因为同源策略的限制,浏览器不能执行其他网站的脚本,但有时候,我们需要访问其他网站的脚本,这时候就需要实现跨域访问。跨域是指从一个域名的网页去请求另一个域名的资源,实现在不同的域之间进行数据传输或通信。主要协议,域名,端口有任何一处不同则被视为跨域。
解决跨域问题的几种方法
参考链接:http://blog.youkuaiyun.com/joyhen/article/details/21631833
参考链接:
http://www.cnblogs.com/2050/p/3191744.html#commentform
参考链接:http://web.jobbole.com/88519/
参考链接:http://web.jobbole.com/88530/
参考链接:
http://blog.youkuaiyun.com/kongjiea/article/details/44201021(分了单向跨域和双向跨域)
需要注意:
协议和端口造成的跨域问题,“前台”是 无能为力的
在跨域问题上,域仅仅是通过“URL”的首部来识别而不会是识别相同的ip地址对应着两个域,或者两个不同的ip地对应着同一个域。
通过jsonp跨域
- 什么是jsonp
jsonp简单地可以理解为json的一种”使用模式“,是应用json的一种方法。jsonp由两部分组成:回调函数和数据。回调函数是当响应到来时应该在页面中调用的函数,而数据就是传入回调函数中的json数据。
- 实现
在js中,我们直接用XMLHttpRequest请求不同域上的数据是不可以的,而在html中,能够实现跨域的几种方式就是:
link属性(css)、href属性(a标签)、src(img标签、script标签)
即在页面上引入不同域的js脚本文件却是可以的,jsonp正好利用了这一特性。
假如a.html页面中,需要利用ajax获取不同域上的json数据,json数据所在地址为http://example.com/data.php ,a.html页面中的代码实现如下:
<script> function dosomething(jsondata){ //处理获取的json数据 } </script> <script src="http://example.com/data.php?callback=dosomething"></script>
js文件载入成功后会执行我们在url参数中指定的函数,并且会把我们需要的json数据作为参数。所以jsonp是需要服务器端的页面进行相应的配合。以php实现为例
<?php $callback = $_GET['callback']; //得到回调函数名 $data=arry('a','b','c'); //要返回的数据 echo $callback.'('.json_encode($data).')';//输出
所以,最终的输出结果是 dosomething([‘a’ , ‘b’ , ‘c’]).
如果使用jquery,在html页面中可以使用下面的代码
<script> $.getJSON('http://example.com/data.php?callback=?',function(){ //出来获得的数据 })
jquery会自动生成一个全局函数来替换callback=?中问号,之后获取数据后会自动销毁,实际上起到一个临时代理函数的作用。$.getJSON会自动判断是否跨域,不跨域的话会调用普通的ajax方法;跨域的话,则会以异步加载js文件的形式来调用jsonp的回调函数。
- jsonp的优缺点
优点:它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的影响;它的兼容性更好,可以在古老的浏览器中运行,不要XMLHttpRequest或ActiveX的支持,在请求完毕后可以通过调用callback的方式回传结果。
缺点:只支持get请求不支持post等其他类型的http请求。它只支持跨域http请求这种情况,不能解决不同域的两个页面之间如何进行javascript调用的问题。
window.name
window对象的name属性是一个特别的属性,当该window的location发生变化,然后重新加载,它的name属性可以依然保持不变。该属性有一个特征:即在一个窗口的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限。
根据window对象的name属性,我们可以在页面A中加载其他域的页面B,而页面B中用javascript把需要传递的数据赋值给window.name ,iframe加载完成之后(iframe.onload),页面A修改iframe的地址,将其变成同域的一个地址,然后就可以读取iframe中的window.name中的值(因为A中的window.name和iframe中的window.name互相独立,所以不能直接在A中获取window.name,而要通过iframe获取window.name)
例如我们要在http://www.example.com/a.html页面通过a.html页面里的js获取另一个位于不同域上的页面www.cnblogs.com/data.html里的数据
在data.html页面中,给当前的window.name设置一个a.html页面想要得到的数据值
<script> window.name='我就是页面a.html想要的数据,所有可以转化成字符串来传递的数据都可以在这里使用,比如传递一个json数据' </script>
在a.html页面中我们不能直接在a.html页面中通过修改window.location来载入data.html页面,在a.html页面中使用一个隐藏的ifrane来充当媒介,由iframe获取data.html的数据,然后a.html再去得到iframe获取到的数据。
iframe只要把src设置成www.cnblogs.com/data.html,就可以获取data.html中通过window.name设置的数据,然后a.html想要得到iframe所获取的数据,也就是想要得到iframe的window.name的值,必须把iframe的src设置成a.html页面同一个域。
如下:
<!doctype html> <html> <head> <meta charset="utf-8"/> <title>window.name跨域</title> <script> function getData(){ //iframe载入data.html页面后悔执行此函数 var iframe=document.getElementById("proxy"); //这个时候a.html和iframe已经处于同一源了,可以互相访问 alert(data);//成功获取data.html中的数据 } iframe.src='b.htm';//这里的b.html为随便的一个页 面,只要与a.html同源就行了,目的是让a.html能访问到iframe里的东西 } </script> </head> <body> <iframe id="proxy" src="http://www.cnblogs.com/data.html" style="display:none" onload="getData()"></iframe>
这种方式适合单向的数据请求,而且协议简单,安全,不像jsonp那样不做限制地执行外部脚本。
document.domain+iframe
对于主域相同,子域不同的两个页面,可以通过设置document.domain的方法解决.具体的做法可以在http://www.a.com/a.html和http://script.a.com/b.html两个文件中分别加上document.domain=’a.com’;然后通过a.html文件中创建一个iframe,去控制iframe的contentDocument,这样两个文件就可以交互了。需要注意的是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(主域不同了)。
做法如下:
在http://www.a.com/a.html页面中设置document.domain:
<iframe src="http://script.a.com/b.html" id="iframe" onload="text()"></iframe> <script> document.domain='a.com';//设置成主域 function test(){ alert(document.getElementById('iframe').contentWindow); } </script>
<script> document.domain='a.com';// 在iframe中载入的这个面 //也设置document.domain,与主页面的document.domain //相同 </script>
这样可以通过js访问到iframe中分各种属性和对象。
另外需要注意的是,修改document.domain的方法只适用于不同子域的框架间的交互,如果你想在http://www.a.com/a.html页面中通过ajax直接请求http://script.a.com/b.html页面是不行的,如果你想通过ajax的方法去与不同子域的页面交互,除了使用jsonp的方法外,还可以用一个隐藏的iframe来做一个代理。
HTML5的postMessage
window.postMessage(message,targetorigin) 方法是html5引入的新特性,它可以用来向其他对象的window对象发送信息,无论这个window对象是属于同源还是不同源,目前IE8+,chrome,firefos,Opera等浏览器都支window.postMessage方法。
在A页面中使用postMessage方法,如下:
window.onload=function(){ var ifr=document.getElementById('ifr'); var targetOrigin="http://wwww.google.com"; ifr.contentWindow.postMessage('hello world!',targetOrigin); }
postMessage的使用方法:
otherWindow.postMessage(message,targetOrigin);
其中otherWindow指目标窗口,也就是给哪个window发消息,是window.frames属性的成员或者由window.open方法创建的窗口。
messgae:指要发送的信息
targetOrigin:限定信息接收范围,若不限制则使用” * “。
B页面通过message事件监听并接收消息:
var onmessage = function(event){ var data = event.data; //消息 var origin=event.origin;//消息来源地址 var source=event.source; //源window对象 if(origin=="http://www.baidu.com"){ console.log(data); //hello world! } }; if(typeof window.addEventListener!='undefined'){ window.addEventListener('message',onmessage,false) }else if(typeof window.addachEvent!='undefined'){ //for ie window.attachEent('onmessage',onmessage); }
其他
跨域的方法有很多,不同的应用场景可以选择不同的跨域方式。除了上面介绍的几种方法外,另外还有其他的跨域方式,比如:flash,服务器代理 ,利用iframe和location.hash等等。有兴趣的同学可以自己搜索,另外上面的参考链接中也有涉及到。