【Geek议题】当年那些风骚的跨域操作
前言现在cross-origin resource sharing(跨域资源共享,下简称CORS)已经十分普及,算上IE8的不标准兼容(XDomainRequest),各大浏览器基本都已支持,当年为了前后端分离、iframe交互和第三方插件开发而头疼跨域是时代已经过去,但当年为了跨域无所不用其极的风骚操作却依然值得学习。 同源策略1995年,同源政策由Netscape公司引入浏览器。目前,所有浏览器都实行这个安全策略。
一个表格看懂什么是同源?
至于为什么说这是个安全策略? 跨域方案同源策略提出的时代还是传统MVC架构(jsp,asp)盛行的年代,那时候的页面靠服务器渲染完成了大部分填充,内容也比较简单,开发者也不会维护独立的API工程,所以其实跨域的需求是比较少的。
于是乎,在没有标准规范的时代,如何解决这些问题的跨域方案就被纷纷提出,可谓百家争鸣,其中不乏令人惊叹的骚操作,这样的极客精神依然值得我们敬佩和学习。 JSON-PJSON-P是各类跨域方案中流行度较高的一个,现在在某些要兼容旧浏览器的环境下还会被使用,著名的jQuery也封装其方法。请勿见名知义,名字中的P是padding“带填充”的意思,这个方法在通信过程中使用的并不是普通的json,而是自带填充功能的JavaScript脚本。 var data = { a: 1,b: 2 }
【PS】
原理及流程
// 定义回调函数 function getTheAnimal(data){ var myAnimal = data.animal; } // 新建标签 var script = document.createElement("script"); script.type = "text/javascript"; // 常用的在url参数部分跟服务器约定号回调函数名 script.src = "http://demo.com/animal.json?callback=getTheAnimal"; document.getElementByTagName('head')[0].appendChild(script); 总结 优点:
缺点:
子域名代理这个方法实际上是利用浏览器允许iframe内的页面只要是跟父页面是同个一级域名下,就能被父页面修改和调用的特点。也许你会疑问,上面讲同源策略的表格中很明确二级域名不同也是算不同源,这岂不矛盾了? 【PS】这里有一个细节,父子页面均要设置 原理及流程
// 最简单的代理文件proxy.html <!DOCTYPE html> <html> <script> document.domain = 'demo.com'; </script> <script src="jquery.min.js"></script> </html> // 新建iframe var iframe = document.createElement('iframe'); // 链接到代理页 iframe.src = 'http://api.demo.com/proxy.html'; // 代理页就绪时触发 iframe.onload = function(){ // 由于代理页已经和父页设置了相同的源,父的脚本可以调用代理页的ajax工具; // 由于是在子页面发起,其请求地址就跟子页面同源了。 iframe.contentWindow.jQuery.ajax({ method: 'POST',url: 'http://api.demo.com/products',data: { product: id,},success: function(){ document.body.removeChild(iframe); /*...*/ } }) } document.getElementsByTagName('head')[0].appendChild(iframe); 总结 优点:
缺点:
模拟form表单form表单的target属性可以指定一个iframe,使主页面不跳转,而iframe内跳转,所以这个方法的核心就是利用表单提交,并在iframe中获取数据。 【PS】form表单提交的特点就是会导致整个页面跳转,返回数据是在新的页面上,这样自然不会产生跨域的问题。 原理及流程
// 新建并隐藏iframe var frame = document.createElement('iframe'); iframe.name = 'post-review'; frame.style.display = 'none'; // 新建表单 var form = document.createElement('form'); form.action = 'http://api.demo.com/products'; form.method = 'POST'; form.target = 'post-review'; // 添加数据 var score = document.createElement('input'); score.name = 'score'; score.value = '5'; // 添加数据 var message = document.createElement('input'); message.name = 'message'; message.value = 'hello world'; // 把数据加到表单 form.appendChild(score); form.appendChild(message); // 渲染iframe和表单 document.body.appendChild(frame); document.body.appendChild(form); // 提交表单发起请求 form.submit(); // 完成清理元素 document.body.removeChild(form); document.body.removeChild(frame); // 最简单返回html <!DOCTYPE html> <html> <script> document.domain = 'demo.com'; window.parent.jsonpCallback('{"status":"success"}'); </script> </html> 总结 由于这个方法是JSON-P与子域名代理的结合版,可以说即拥有两者的优点,也保留了两者一些缺点。 优点:
缺点:
window.name这方法利用了 【PS】例子里演示的是发起get请求,只要把请求地址直接写到src里就行了。如果想要发起其他类型的请求,可以类比采用模拟的form的方式进行改造。 原理及流程
// 新建iframe var iframe = document.createElement('iframe'); var body = document.getElementByTagName('body'); // 隐藏iframe并链接地址 iframe.style.display = 'none'; iframe.src = 'http://api.demo.com/server.html?id=1'; // 因为需要两次跳转,这里有个完成标记 var done = fasle; // 这里会触发至少两次,一次由于非同源是取不到值的。 iframe.onload = iframe.onreadystatechange = function(){ if(! this.readyState && (iframe.readyState !== 'complete' || done)){ return; } console.log('Listening'); var name = iframe.contentWindow.name; if(name){ console.log(iframe.contentWindow.name); done = true; } }; body.appendChild(iframe); // 最简单返回html <!DOCTYPE html> <html> <script> function init(){ window.name = 'hello'; window.location = 'http://demo.com/empty.html' } </script> <body onload="init();"></body> </html> 总结 优点:
缺点:
window.hash这个方法利用了location的特性:不同域的页面,可以写不可读。而只改变哈希部分(井号后面)不会导致页面跳转。也就是可以让父、子页面互相写对方的location的哈希部分,进行通讯。 原理及流程
【PS】父页面会循环检查哈希是否改变来读取值,因为这种降级方案的使用环境一般是不会有hashchange事件的。演示里是最简单的get方法,如果想要发起其他类型的请求,可以类比采用模拟的form的方式进行改造,但记住不要丢失父页面的url。
// 获取当前url var url = window.location.href; // 新建iframe var iframe = document.createElement('iframe'); // 隐藏iframe并设置链接,把当前url带上 iframe.style.display = 'none'; iframe.src = 'http://api.demo.com/server.html?id=1&url=' + encodeURIComponent(url); var body = document.getElementByTagName('body')[0]; body.appendChild(iframe); // 循环监听处理 var listener = function(){ // 读取 var hash = location.hash; // 还原 if(hash && hash !== '#'){ console.log(hash.replace('#','')); window.loacation.href = url + '#'; } // 继续监听 setTimeout(listener,100); }; listener(); // 最简单返回html <!DOCTYPE html> <html> <script> function init(){ // 剪裁出父页面的url var parentUrl = ''; var url = window.location.href; var str = url.split('?')[1].replace('?',''); strs = str.split("&"); for(var i = 0; i < strs.length; i ++) { if(strs.split("=")[0] === 'url'){ parentUrl = strs.split("=")[1]; } } // 设置到父页面上 window.parent.location = decodeURIComponent(parentUrl) + '#helloworld'; } </script> <body onload="init();"></body> </html> 总结 优点:
缺点:
现代的标准W3C的标准化跨域方案,让现代浏览器跨域已经不是什么复杂的事。这部分网上资料已经很多,这里就只是简单介绍。 CORSCORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。 CORS参考文档 postMessageH5的window.postMessage为浏览器带来了一个安全的。基于事件的消息api。 postMessage参考文档 安全问题上述的各类非标准的骚操作,都算是对同源策略的破解办法,在方便开发者完成跨域目的的同时,各类恶意的攻击者也自然会利用这些方案为非作歹。 其中子域名代理的风险最低,因为需要服务器设置特定的子域名,也就是已经是两个源的协商结果,一般黑客是难以模拟的。 风险最高的要算JSON-P的方案,因为这是任何客户端都可随意使用的办法,CSRF攻击的核心也是利用了特定标签的跨域性发起请求,所以JSON-P最好用在无用户状态的低安全性API上。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |