轉(zhuǎn)帖|其它|編輯:郝浩|2011-02-25 09:51:19.000|閱讀 472 次
概述:JavaScript出于安全方面的考慮,不允許跨域調(diào)用其他頁(yè)面的對(duì)象。但在安全限制的同時(shí)也給注入iframe或是ajax應(yīng)用上帶來(lái)了不少麻煩。這里把涉及到跨域的一些問題簡(jiǎn)單地整理一下。
# 界面/圖表報(bào)表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
什么是跨域
JavaScript出于安全方面的考慮,不允許跨域調(diào)用其他頁(yè)面的對(duì)象。但在安全限制的同時(shí)也給注入iframe或是ajax應(yīng)用上帶來(lái)了不少麻煩。這里把涉及到跨域的一些問題簡(jiǎn)單地整理一下:
首先什么是跨域,簡(jiǎn)單地理解就是因?yàn)镴avaScript同源策略的限制,a.com 域名下的js無(wú)法操作b.com或是c.a.com域名下的對(duì)象。更詳細(xì)的說(shuō)明可以看下表:
特別注意兩點(diǎn):
第一,如果是協(xié)議和端口造成的跨域問題前臺(tái)是無(wú)能為力的,
第二:在跨域問題上,域僅僅是通過URL的首部來(lái)識(shí)別而不會(huì)去嘗試判斷相同的ip地址對(duì)應(yīng)著兩個(gè)域或兩個(gè)域是否在同一個(gè)ip上。
URL的首部指window.location.protocol +window.location.host,也可以理解為Domains, protocols and ports must match。
接下來(lái)簡(jiǎn)單地總結(jié)一下在前臺(tái)一般處理跨域的辦法,后臺(tái)proxy這種方案牽涉到后臺(tái)配置,這里就不闡述了,有興趣的可以看看yahoo的這篇文章:《JavaScript: Use a Web Proxy for Cross-Domain XMLHttpRequest Calls》
1、document.domain+iframe的設(shè)置
對(duì)于主域相同而子域不同的例子,可以通過設(shè)置document.domain的辦法來(lái)解決。具體的做法是可以在//www.a.com/a.html和//script.a.com/b.html兩個(gè)文件中分別加上document.domain = a.com;然后通過a.html文件中創(chuàng)建一個(gè)iframe,去控制iframe的contentDocument,這樣兩個(gè)js文件之間就可以交互了。當(dāng)然這種辦法只能解決主域相同而二級(jí)域名不同的情況,如果你異想天開的把script.a.com的domian設(shè)為alibaba.com那顯然是會(huì)報(bào)錯(cuò)地!代碼如下:
www.a.com上的a.html
document.domain = 'a.com'; var ifr = document.createElement('iframe'); ifr.src = '//script.a.com/b.html'; ifr.style.display = 'none'; document.body.appendChild(ifr); ifr.onload = function(){ var doc = ifr.contentDocument || ifr.contentWindow.document; // 在這里操縱b.html alert(doc.getElementsByTagName("h1")[0].childNodes[0].nodeValue); }; script.a.com上的b.html document.domain = 'a.com';
這種方式適用于{www.kuqin.com, kuqin.com, script.kuqin.com, css.kuqin.com}中的任何頁(yè)面相互通信。
備注:某一頁(yè)面的domain默認(rèn)等于window.location.hostname。主域名是不帶www的域名,例如a.com,主域名前面帶前綴的通常都為二級(jí)域名或多級(jí)域名,例如www.a.com其實(shí)是二級(jí)域名。 domain只能設(shè)置為主域名,不可以在b.a.com中將domain設(shè)置為c.a.com。
問題:
1、安全性,當(dāng)一個(gè)站點(diǎn)(b.a.com)被攻擊后,另一個(gè)站點(diǎn)(c.a.com)會(huì)引起安全漏洞。
2、如果一個(gè)頁(yè)面中引入多個(gè)iframe,要想能夠操作所有iframe,必須都得設(shè)置相同domain。
2、動(dòng)態(tài)創(chuàng)建script
雖然瀏覽器默認(rèn)禁止了跨域訪問,但并不禁止在頁(yè)面中引用其他域的JS文件,并可以自由執(zhí)行引入的JS文件中的function(包括操作cookie、Dom等等)。根據(jù)這一點(diǎn),可以方便地通過創(chuàng)建script節(jié)點(diǎn)的方法來(lái)實(shí)現(xiàn)完全跨域的通信。具體的做法可以參考YUI的Get Utility
這里判斷script節(jié)點(diǎn)加載完畢還是蠻有意思的:ie只能通過script的readystatechange屬性,其它瀏覽器是script的load事件。以下是部分判斷script加載完畢的方法。
js.onload = js.onreadystatechange = function() { if (!this.readyState || this.readyState === 'loaded' || this.readyState === 'complete') { // callback在此處執(zhí)行 js.onload = js.onreadystatechange = null; } };
3、利用iframe和location.hash
這個(gè)辦法比較繞,但是可以解決完全跨域情況下的腳步置換問題。原理是利用location.hash來(lái)進(jìn)行傳值。在url: //a.com#helloword中的#helloworld就是location.hash,改變hash并不會(huì)導(dǎo)致頁(yè)面刷新,所以可以利用hash值來(lái)進(jìn)行數(shù)據(jù)傳遞,當(dāng)然數(shù)據(jù)容量是有限的。假設(shè)域名a.com下的文件cs1.html要和cnblogs.com域名下的cs2.html傳遞信息,cs1.html首先創(chuàng)建自動(dòng)創(chuàng)建一個(gè)隱藏的iframe,iframe的src指向cnblogs.com域名下的cs2.html頁(yè)面,這時(shí)的hash值可以做參數(shù)傳遞用。
cs2.html響應(yīng)請(qǐng)求后再將通過修改cs1.html的hash值來(lái)傳遞數(shù)據(jù)(由于兩個(gè)頁(yè)面不在同一個(gè)域下IE、Chrome不允許修改parent.location.hash的值,所以要借助于a.com域名下的一個(gè)代理iframe;Firefox可以修改)。同時(shí)在cs1.html上加一個(gè)定時(shí)器,隔一段時(shí)間來(lái)判斷l(xiāng)ocation.hash的值有沒有變化,一點(diǎn)有變化則獲取獲取hash值。代碼如下:
先是a.com下的文件cs1.html文件:
function startRequest(){ var ifr = document.createElement('iframe'); ifr.style.display = 'none'; ifr.src = '//www.cnblogs.com/lab/cscript/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); cnblogs.com域名下的cs2.html: //模擬一個(gè)簡(jiǎn)單的參數(shù)處理操作 switch(location.hash){ case '#paramdo': callBack(); break; case '#paramset': //do something…… break; } function callBack(){ try { parent.location.hash = 'somedata'; } catch (e) { // ie、chrome的安全機(jī)制無(wú)法修改parent.location.hash, // 所以要利用一個(gè)中間的cnblogs域下的代理iframe var ifrproxy = document.createElement('iframe'); ifrproxy.style.display = 'none'; ifrproxy.src = '//a.com/test/cscript/cs3.html#somedata'; // 注意該文件在"a.com"域下 document.body.appendChild(ifrproxy); } }
a.com下的域名cs3.html
//因?yàn)閜arent.parent和自身屬于同一個(gè)域,所以可以改變其location.hash的值 parent.parent.location.hash = self.location.hash.substring(1);
當(dāng)然這樣做也存在很多缺點(diǎn),諸如數(shù)據(jù)直接暴露在了url中,數(shù)據(jù)容量和類型都有限等……
4、window.name實(shí)現(xiàn)的跨域數(shù)據(jù)傳輸
文章較長(zhǎng)列在此處不便于閱讀,詳細(xì)請(qǐng)看 window.name實(shí)現(xiàn)的跨域數(shù)據(jù)傳輸。
5、使用HTML5 postMessage
HTML5中最酷的新功能之一就是 跨文檔消息傳輸Cross Document Messaging。下一代瀏覽器都將支持這個(gè)功能:Chrome 2.0+、Internet Explorer 8.0+, Firefox 3.0+, Opera 9.6+, 和 Safari 4.0+ 。 Facebook已經(jīng)使用了這個(gè)功能,用postMessage支持基于web的實(shí)時(shí)消息傳遞。
otherWindow.postMessage(message, targetOrigin);
otherWindow: 對(duì)接收信息頁(yè)面的window的引用。可以是頁(yè)面中iframe的contentWindow屬性;window.open的返回值;通過name或下標(biāo)從window.frames取到的值。
message: 所要發(fā)送的數(shù)據(jù),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 = '//b.com'; // 若寫成'//b.com/c/proxy.html'效果一樣 // 若寫成'//c.com'就不會(huì)執(zhí)行postMessage了 ifr.contentWindow.postMessage('I was there!', targetOrigin); }; /script
b.com/index.html中的代碼:
script type="text/javascript" window.addEventListener('message', function(event){ // 通過origin屬性判斷消息來(lái)源地址 if (event.origin == '//a.com') { alert(event.data); // 彈出"I was there!" alert(event.source); // 對(duì)a.com、index.html中window對(duì)象的引用 // 但由于同源策略,這里event.source不可以訪問window對(duì)象 } }, false); /script
6、利用flash
這是從YUI3的IO組件中看到的辦法,具體可見//developer.yahoo.com/yui/3/io/。
可以看在Adobe Developer Connection看到更多的跨域代理文件規(guī)范:ross-Domain Policy File Specifications、HTTP Headers Blacklist。
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請(qǐng)務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請(qǐng)郵件反饋至chenjj@fc6vip.cn
文章轉(zhuǎn)載自:網(wǎng)絡(luò)轉(zhuǎn)載