js实现前端跨域
来源:互联网 发布:经济学常用微观数据库 编辑:程序博客网 时间:2024/05/16 16:57
摘自http://www.cnblogs.com/rainman/archive/2011/02/20/1959325.html#undefined
和http://www.cnblogs.com/2050/p/3191744.html#commentform
什么是跨域
JavaScript出于安全方面的考虑,不允许跨域调用其他页面的对象。但在安全限制的同时也给注入iframe或是ajax应用上带来了不少麻烦。这里把涉及到跨域的一些问题简单地整理一下:
首先什么是跨域,简单地理解就是因为JavaScript同源策略的限制,a.com 域名下的js无法操作b.com或是c.a.com域名下的对象。更详细的说明可以看下表:
http://www.a.com/b.js同一域名下允许http://www.a.com/lab/a.js
http://www.a.com/script/b.js同一域名下不同文件夹允许http://www.a.com:8000/a.js
http://www.a.com/b.js同一域名,不同端口不允许http://www.a.com/a.js
https://www.a.com/b.js同一域名,不同协议不允许http://www.a.com/a.js
http://70.32.92.74/b.js域名和域名对应ip不允许http://www.a.com/a.js
http://script.a.com/b.js主域相同,子域不同不允许http://www.a.com/a.js
http://a.com/b.js同一域名,不同二级域名(同上)不允许(cookie这种情况下也不允许访问)http://www.cnblogs.com/a.js
http://www.a.com/b.js不同域名不允许
- 特别注意两点:
- 第一,如果是协议和端口造成的跨域问题“前台”是无能为力的,
- 第二:在跨域问题上,域仅仅是通过“URL的首部”来识别而不会去尝试判断相同的ip地址对应着两个域或两个域是否在同一个ip上。
“URL的首部”指window.location.protocol +window.location.host,也可以理解为“Domains, protocols and ports must match”。
接下来简单地总结一下在“前台”一般处理跨域的办法,后台proxy这种方案牵涉到后台配置,这里就不阐述了,有兴趣的可以看看yahoo的这篇文章:《JavaScript: Use a Web Proxy for Cross-Domain XMLHttpRequest Calls》
1、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,这样两个文件之间就可以‘交互’了。
www.a.com上的a.html
document.domain = 'a.com';
var ifr = document.createElement('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.docment;
//do someThing
}
script.a.com上的b.html
document.domain = 'a.com';
备注:某一页面的domain默认等于window.location.hostname。主域名是不带www的域名,例如a.com,主域名前面带前缀的通常都为二级域名或多级域名,例如www.a.com其实是二级域名。domain只能设置为主域名,不可以在b.a.com中将domain设置为c.a.com。
- 问题:
- 1、安全性,当一个站点(b.a.com)被攻击后,另一个站点(c.a.com)会引起安全漏洞。
- 2、如果一个页面中引入多个iframe,要想能够操作所有iframe,必须都得设置相同domain。
虽然浏览器默认禁止了垮与访问,但并不禁止在页面中引用其他域的JS文件,并可以自由执行引入的JS文件中的function(包括cookie、Dom等)。根据这一点,可以方便地通过创建script节点的方法来实现完全跨域的通信。
3.根据iframe和location.hash
利用location.hash进行传值,改变hash不会导致页面刷新,数据容量有限。a.com下的 cs1.html要和b.com下的cs2.html传递信息,在cs1.html中创建的iframe src=“/b.com/cs2.html#hash”, 利用hash进行传值。cs2.html响应请求后将通过修改cs1.html的hash值来传递数据(由于两个页面不在同一个域下IE、Chrome不允许修改parent.location.hash的值,所以要借助于a.com域名下的一个代理iframe;Firefox可以修改)。同时在cs1.html上加一个定时器,隔一段时间来判断location.hash的值有没有变化,一点有变化则获取获取hash值。代码如下:
a.cpm下的 cs1.html
function startRequest () { var ifr = document.createElement('iframe'); ifr.style.dispaly = 'none'; ifr.src = 'http://b.com/cs2.html#paramdo'; document.body.appendChild(ifr); }function checkHash() { try { var data = location.hash?location.hash.substring(1): ''; if (console.log) { console.log('Now the data is' + data); } } catch(e) {};}setInterval(checkHash, 2000);
cnlogs.com域名下的cs2.html:
switch(location.hash) { case: '#paramdo': callBack(); break; case '#paramset': //do something break;}function callback () { try { parent.location.hash = 'someData'; } catch(e) { // ie、chrome的安全机制无法修改parent.location.hash, // 所以要利用一个中间的b域下的代理iframe var ifrproxy = document.createElement('iframe'); ifrproxy.style.display = 'none'; ifrproxy.src = 'http://a.com/test/cscript/cs3.html#somedata'; // 注意该文件在"a.com"域下 document.body.appendChild(ifrproxy); }}
a.com下的域名css3.html
//因为parent.parent和自身属于同一个域,所以可以改变其location.hash的值parent.parent.location.hash = self.location.hash.substring(1);缺点:将参数暴露在url,数据容量和类型都有限等……
4、window.name实现的跨域数据传输
window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。
比如:有一个页面a.html,它里面有这样的代码:
再看看b.html页面的代码:
我们看到在b.html页面上成功获取到了它的上一个页面a.html给window.name设置的值。如果在之后所有载入的页面都没对window.name进行修改的话,那么所有这些页面获取到的window.name的值都是a.html页面设置的那个值。当然,如果有需要,其中的任何一个页面都可以对window.name的值进行修改。注意,window.name的值只能是字符串的形式,这个字符串的大小最大能允许2M左右甚至更大的一个容量,具体取决于不同的浏览器,但一般是够用了。
上面的例子中,我们用到的页面a.html和b.html是处于同一个域的,但是即使a.html与b.html处于不同的域中,上述结论同样是适用的,这也正是利用window.name进行跨域的原理。
下面就来看一看具体是怎么样通过window.name来跨域获取数据的。还是举例说明。
比如有一个www.example.com/a.html页面,需要通过a.html页面里的js来获取另一个位于不同域上的页面www.cnblogs.com/data.html里的数据。
data.html页面里的代码很简单,就是给当前的window.name设置一个a.html页面想要得到的数据值。data.html里的代码:
那么在a.html页面中,我们怎么把data.html页面载入进来呢?显然我们不能直接在a.html页面中通过改变window.location来载入data.html页面,因为我们想要即使a.html页面不跳转也能得到data.html里的数据。答案就是在a.html页面中使用一个隐藏的iframe来充当一个中间人角色,由iframe去获取data.html的数据,然后a.html再去得到iframe获取到的数据。
充当中间人的iframe想要获取到data.html的通过window.name设置的数据,只需要把这个iframe的src设为www.cnblogs.com/data.html就行了。然后a.html想要得到iframe所获取到的数据,也就是想要得到iframe的window.name的值,还必须把这个iframe的src设成跟a.html页面同一个域才行,不然根据前面讲的同源策略,a.html是不能访问到iframe里的window.name属性的。这就是整个跨域过程。
看下a.html页面的代码:
上面的代码只是最简单的原理演示代码,你可以对使用js封装上面的过程,比如动态的创建iframe,动态的注册各种事件等等,当然为了安全,获取完数据后,还可以销毁作为代理的iframe。网上也有很多类似的现成代码,有兴趣的可以去找一下。
通过window.name来进行跨域,就是这样子的。
5、使用HTML5 postMessage
HTML5中最酷的新功能之一就是跨文档消息传输Cross Document Messaging。下一代浏览器都将支持这个功能:Chrome 2.0+、Internet Explorer 8.0+, Firefox 3.0+, Opera 9.6+, 和 Safari 4.0+ 。 Facebook已经使用了这个功能,用postMessage支持基于web的实时消息传递。
- otherWindow.postMessage(message, targetOrigin);
- otherWindow: 对接收信息页面的window的引用。可以是页面中iframe的contentWindow属性;window.open的返回值;通过name或下标从window.frames取到的值。
message: 所要发送的数据,string类型。
targetOrigin: 用于限制otherWindow,“*”表示不作限制
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'); var targetOrigin = "http://b.com"; //若写成'http://b.com/c/proxy.html'效果一样 //若写成'http://c.com'就不会执行postMessage了 ifr.contentWindow.postMessage('', targetOrigin);};</script>
b.com/index.html中的代码
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>
参考文章:《精通HTML5编程》第五章——跨文档消息机制、https://developer.mozilla.org/en/dom/window.postmessage
6、利用flash
这是从YUI3的IO组件中看到的办法,具体可见http://developer.yahoo.com/yui/3/io/。
可以看在Adobe Developer Connection看到更多的跨域代理文件规范:ross-Domain Policy File Specifications、HTTP Headers Blacklist。
- js实现前端跨域
- js使用JSONP、VAR实现前端跨域
- 前端JS跨域请求
- JS学习35:js使用JSONP、VAR实现前端跨域
- js分页实现,前端实现。
- [前端] JS实现文件下载
- 使用JS实现前端缓存
- 使用js实现前端缓存
- JS-前端实现图片上传
- 前端常见算法js实现
- [JS]前端下载的实现
- 前端js实现桌面通知
- JS前端模板引擎实现
- Director JS 实现前端路由
- js前端实现模糊查询
- js 前端实现国际化配置
- js实现前端数据分页
- 使用JS实现前端缓存
- 遗传算法实现之python VS matlab
- WebView的使用总结-js与java之前的相互调用
- 有用的网站
- Java多线程——thread及runnable的基本使用及交替执行
- 【超强干货】蘑菇街App的组件化之路
- js实现前端跨域
- gradle多渠道打包apk小贴士
- Spark编程指南V1.4.0(翻译)
- 判断s所指的字符串是否是回文数
- Android Studio的基本使用
- 通过一个大型项目来学习分布式算法(4)
- 【胖鱼头】配置Maven站点并使用ssh发布到服务器
- linux c语言 select函数用法
- 安装CocoaPods