跨域解决方案和实践
2017-02-25 14:47
183 查看
document.domian + iframe跨子域
这种方法对于主域相同而子域不同的例子,可以通过设置document.domain的办法解决。具体做法:
可以在http://www.a.com/a.html和http://script.a.com/b.html两个文件中分别加上document.domain = ‘a.com’;然后通过a.html文件中创建一个iframe,去控制iframe的contentDocument,这样两个js文件之间就可以“交互”了。当然这种办法只能解决主域相同而二级域名不同的情况代码如下:
www.a.com/a.html
document.domain="a.com"; var ifr=document.createElement('iframe'); //把b.html作为iframe载入了 ifr.src='http://script.a.com/b.html'; ifr.style.display='none'; document.body.appendChild(ifr); ifr.onload=function(){ var doc=ifr.contentDocument||ifr.contentWindow.document; //... }
script.a.com上的b.html
document.domain='a.com';
解释
document.domain ,我们是设置一个页面的域名,某一个页面的域名默认为:window.location.hostname即默认下:
document.domain===window.location.hostname //true
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,因为主域已经不相同了。
安全性
安全性,当一个站点(b.a.com)被攻击后,另一个站点(c.a.com)会引起安全漏洞。
操作
如果要一个页面中引入多个iframe,要想能够操作所有iframe,必须都得设置相同domain。
iframe与Ajax
光设置了document.domain,在不同的子域名之间还是不能直接通过Ajax来访问的,但是我们可以通过iframe这个中间人来做到。比如A.com使用脚本获取api.A.com的数据,那么我们可以用iframe来载入这个api.A.com的页面,由于我们修改了document.domain之后我们当前这个页面与iframe是可以通过js交互的,我们的js可以完全控制整个iframe,所以可以让iframe去发ajax请求然后收到的数据我们也可以获得了。
window.name跨域
原理
window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。换言之
也就是说我们在一个窗口中开了N个iframe,N个iframe中的页面中的window都可以访问window.name属性。
这里我们有上个页面:
index.html
b.html
proxy.html
我们可以在index.html中设置一个iframe,它的src一开始是我们的b.html,然后我们通过iframe去访问这个b.html设置的window.name的值。等待加载完毕后我们又把这个src设置成同域下的proxy.html
这时我们才能访问iframe.contentWindow.name中的数据。
因为直接在index.html访问不同域名的b.html是无法访问到window的属性的。
另一个域名的 b.html
<script type="text/javascript"> window.name = 'I was there!'; // 这里是要传输的数据,大小一般为2M,IE和firefox下可以大至32M左右 // 数据格式可以自定义,如json、字符串 </script>
一开始我直接让index.html访问b.html如下:
index.html的脚本var URL= 'http://spw.linzhida.cc/b.html'; var ifr=document.createElement('iframe'); document.body.appendChild(ifr); var loadfn=function(){ var ifrwindow=ifr.contentWindow; var data=ifrwindow.name;//load data! console.log(data); } var removeWindow=function(ifr){ ifr.contentWindow.document.write(' '); ifr.contentWindow.close(); document.removeChild(ifr); } //添加事件给ifr! //ifr.addEventListener('DOMContentLoaded', loadfn); //给iframe应该添加的是onload! DOMContentLoaded不准确也没效果 ifr.onload=function(){ loadfn(); } // ifr.src=URL;
报错:
后来修改后的index.html的脚本
var URL= 'http://spw.linzhida.cc/b.html'; var sameOrigin='http://www.linzhida.cc/Proxy.html'; /** * 必须是在ifr的src已经设置了之后才会能访问到window?? 不是,只要创建了iframe并且添加到了当前页面之后就可以访问到了 or document */ var ifr=document.createElement('iframe'), state=0; document.body.appendChild(ifr); var loadfn=function(){ var ifrwindow=ifr.contentWindow; if(state==1){ var data=ifrwindow.name;//load data! console.log(data); }else if(state==0){ ifrwindow.location=sameOrigin; //设置代理文件 state=1; } } var removeWindow=function(ifr){ ifr.contentWindow.document.write(' '); ifr.contentWindow.close(); document.removeChild(ifr); } //添加事件给ifr! //ifr.addEventListener('DOMContentLoaded', loadfn); //给iframe应该添加的是onload! DOMContentLoaded不准确也没效果 ifr.onload=function(){ loadfn(); } ifr.src=URL; //事先设置了第一次的src
proxy.html中无内容
总结起来即:iframe的src属性由外域转向本地域,跨域数据即由iframe的window.name从外域传递到本地域。这个就巧妙地绕过了浏览器的跨域访问限制,但同时它又是安全操作。
HTML5新方法window.postMessage
跨文档消息传输! Cross Document Message简称XDM,是指来自不同域的页面间传递消息。
主要两个目的地点
对于XDM来说,一个是当前页面的iframe元素
一个是当前页面弹出的窗口。
这两个中的都可能是不同域的url,所以我们需要实现这种不同域的通信。
postMessage()方法接收两个参数:一个消息和一个表示消息接收方来自哪个域的字符串:
// // Facebook已经使用了这个功能, // 用postMessage支持基于web的实时消息传递。 /* otherWindow.postMessage(message, targetOrigin); otherWindow: 对接收信息页面的window的引用。可以是页面中iframe的contentWindow属性;window.open的返回值;通过name或下标从window.frames取到的值。 message: 所要发送的数据,string类型。 targetOrigin: 用于限制otherWindow,“*”表示不作限制 */ var URL='http://sxw.linzhida.cc/b.html'; var ORIGIN='http://sxw.linzhida.cc', rootDiv=document.getElementById('root'); function createIframe(){ var ifr=document.createElement('iframe'); rootDiv.appendChild(ifr); ifr.src=URL; ifr.contentDocument.domain='linzhida.cc'; return ifr; } var ifr=createIframe(); //iframe的src打开的页面需要和 你postMessage指定的ORIGIN是一个域名 window.onload=function(){ //var targetOrigin=URL; //URL.split('.'); ifr.contentWindow.postMessage('i was here',ORIGIN); }
错误
原因
这时因为我们的postMessage方法需要iframe中的window加载完才能接收到消息。
所以你必须将window改成ifr的onload事件
//变量提升,是对声明而言,如果你在声明之前进行RHS去取源值 //变量声明会被提升,但是变量赋值不会 var ifr=createIframe(); //iframe的src打开的页面需要和 你postMessage指定的ORIGIN是一个域名 ifr.onload=function(){ //var targetOrigin=URL; //URL.split('.'); ifr.contentWindow.postMessage('i was here',ORIGIN); }
接收端http://sxw.linzhida.cc/b.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>B page</title> </head> <body> <script type="text/javascript"> var URL='http://www.linzhida.cc'; //document.domain = 'linzhida.cc'; window.name="data is here!"; window.addEventListener("message", receiveMessage, false); function receiveMessage(event){ var origin=event.origin||event.originalEvent.origin;//For Chrome, the origin property is in the event.originalEvent object. if(origin!==URL){ return; } //event.data 发送的数据 //event.origin 发送端窗口的origin , //event.source reference of the window object that send the message console.log(event.data); console.log(event.source); } </script> </body> </html>
注意:
第二个参数targetOrigin可以设置为*,这样就会把消息发送给来自任何域的文档,但不推荐。尽量提供一个准确的targetOrigin,如果你知道要发送的window的Document的URI,这样可以保证安全性。上面所说的向其他window对象发送消息,其实就是指一个页面有几个框架的那种情况,因为每一个框架都有一个window对象。在讨论第二种方法的时候,我们说过,不同域的框架间是可以获取到对方的window对象的,而且也可以使用window.postMessage这个方法。
关于接受端的最佳实践
如果你要接受其他域or站点的数据,总是验证发送端的origin和source属性,不要监听来自任何域的message事件。 不然会造成一个跨站脚本漏洞。优点:
设置成特定的域,防止浏览器把消息发送到不安全的地方,保障安全通信。来源匹配才会把消息传递给内嵌的框架(iframe)中,否则postMessage什么也不做。 这一限制可以避免窗口中的位置在你不知情的情况下发生改变。
包含iframe的页面可以确保自身不受恶意内容的侵扰,因为它只通过XDM和嵌入的框架通信。相同域的页面也可以用XDM
缺点:
event.source可能会被误认为是window对象。但是它实际只是window对象的一个代理,我们不能通过这个代理对象访问window对象的其他任何信息。postMessage的第一个参数永远字符串。 后来这个参数定义又改了,可以传入任何数据结构,但是浏览器并非都实现了这个变化。最稳妥的做法还是先通过JSON.stringify()来包装这个数据。然后在onmessage事件中JSON.parse
兼容性:IE8+
iframe和location.hash
这个方法不细说了。 原理还是类似:我们利用location.hash这个属性来传值,比如url:
http://a.com#hello 中的这个hello就是在a.com下访问location.hash的值,我们可以在a.com的index.html访问另一个域名如b.com的c.html,还是通过设置一个隐藏的iframe,将这个iframe的src设置成b.com/c.html, 然后这个hash值就可以作为参数传递用。
因为两个不同域的页面不能修改parent.location.hash的值
缺点:
1.数据直接暴露在url中,每次都是设置index.html的hash值来传递数据2. 传递的数据的大小有限,因为浏览器对url的长度有限制
相关文章推荐
- SpringBoot实践之---前端跨域问题的解决方案
- JQuery中Ajax跨域解决方案 关于ajax跨域操作的jquery, django实践 基于jquery的ajax之跨域解决方案
- json跨域(2)----js跨域及解决方案
- Javascript跨域访问解决方案
- JS跨域解决方案之使用CORS实现跨域
- C#进阶系列——WebApi 跨域问题解决方案:CORS
- Tensorflow实践一:mac环境搭建问题解决方案
- CORS 跨域 实现思路及相关解决方案
- WCF Restful调用跨域解决方案
- 前端筑基篇(一)->ajax跨域原理以及解决方案
- jquery跨域和javascript实践指南
- JQuery的Ajax跨域请求的解决方案
- jQuery JSONP 跨域实践
- PHP Ajax 跨域问题最佳解决方案
- Web学习之跨域问题及解决方案
- AJAX跨域调用ASP.NET MVC或者WebAPI服务的问题及解决方案
- Javascript跨域访问解决方案
- 同源策略与跨域解决方案
- Javascript跨域和Ajax跨域解决方案
- asp.net web api2.0 ajax跨域解决方案