js實(shí)現(xiàn)跨域的多種方法
從域說起
域: 域是WIN2K網(wǎng)絡(luò)系統(tǒng)的安全性邊界。我們知道一個(gè)計(jì)算機(jī)網(wǎng)最基本的單元就是“域”,這一點(diǎn)不是WIN2K所獨(dú)有的,但活動(dòng)目錄可以貫穿一個(gè)或多個(gè)域。在獨(dú)立的計(jì)算機(jī)上,域即指計(jì)算機(jī)本身,一個(gè)域可以分布在多個(gè)物理位置上,同時(shí)一個(gè)物理位置又可以劃分不同網(wǎng)段為不同的域,每個(gè)域都有自己的安全策略以及它與其他域的信任關(guān)系。當(dāng)多個(gè)域通過信任關(guān)系連接起來之后,活動(dòng)目錄可以被多個(gè)信任域域共享
域樹:域樹由多個(gè)域組成,這些域共享同一表結(jié)構(gòu)和配置,形成一個(gè)連續(xù)的名字空間。樹中的域通過信任關(guān)系連接起來,活動(dòng)目錄包含一個(gè)或多個(gè)域樹。域樹中的域是通過雙向可傳遞信任關(guān)系連接在一起。由于這些信任關(guān)系是雙向的而且是可傳遞的,因此在域樹或樹林中新創(chuàng)建的域可以立即與域樹或樹林中每個(gè)其他的域建立信任關(guān)系。這些信任關(guān)系允許單一登錄過程,在域樹或樹林中的所有域上對(duì)用戶進(jìn)行身份驗(yàn)證,但這不一定意味著經(jīng)過身份驗(yàn)證的用戶在域樹的所有域中都擁有相同的權(quán)利和權(quán)限。因?yàn)橛蚴前踩缦?,所以必須在每個(gè)域的基礎(chǔ)上為用戶指派相應(yīng)的權(quán)利和權(quán)限。
域樹中的域?qū)哟卧缴罴?jí)別越低,一個(gè)“.”代表一個(gè)層次。
如域zhidao.baidu.com(百度知道)就比 baidu.com(百度)這個(gè)域級(jí)別低,因?yàn)樗袃蓚€(gè)層次關(guān)系,而baidu.com只有一個(gè)層次。
何為跨域
默認(rèn)情況下,,XHR 對(duì)象只能訪問與包含它的頁(yè)面位于同一個(gè)域中的資源。這種安全策略可以預(yù)防某些惡意行為。但是,實(shí)現(xiàn)合理的跨域請(qǐng)求對(duì)開發(fā)某些瀏覽器應(yīng)用程序也是至關(guān)重要的。
只要協(xié)議、域名、端口有任何一個(gè)不同,都被當(dāng)作是不同的域
比如在http://www.a.com/a.js 頁(yè)面向以下頁(yè)面發(fā)送一個(gè)ajax請(qǐng)求,以下是其請(qǐng)求結(jié)果及說明
對(duì)于端口和協(xié)議的不同,只能通過后臺(tái)來解決。我們要解決的是域名不同的問題
如何跨域
(一) CORS(Cross-Origin Resource Sharing,跨源資源共享)
1.CORS(Cross-Origin Resource Sharing,跨源資源共享)是W3C 的一個(gè)工作草案,定義了在必須訪問跨源資源時(shí),瀏覽器與服務(wù)器應(yīng)該如何溝通。CORS 背后的基本思想,就是使用自定義的HTTP 頭部讓瀏覽器與服務(wù)器進(jìn)行溝通,從而決定請(qǐng)求或響應(yīng)是應(yīng)該成功,還是應(yīng)該失敗。
2.實(shí)現(xiàn)此功能非常簡(jiǎn)單,只需由服務(wù)器發(fā)送一個(gè)響應(yīng)標(biāo)頭即可。
瀏覽器支持情況:
- IE 8+
- Firefox 3.5+
- Opera 12+
- Safari 4+
- Chrome 3+
假設(shè)我們頁(yè)面或者應(yīng)用已在 http://www.a.com/ 上了,而我們打算從 http://www.b.com 請(qǐng)求提取數(shù)據(jù)。一般情況下,如果我們直接使用 AJAX 來請(qǐng)求將會(huì)失敗,瀏覽器也會(huì)返回錯(cuò)誤。
利用 CORS,http://www.b.com 只需添加一個(gè)標(biāo)頭,就可以允許來自 http://www.a.com 的請(qǐng)求。
下面是用php進(jìn)行的設(shè)置,“*”號(hào)表示允許任何域向我們的服務(wù)端提交請(qǐng)求:
header{"Access-Control-Allow-Origin: *"}
CORS的兼容性寫法
function createCORSRequest(method, url){ var xhr = new XMLHttpRequest(); //非IE瀏覽器 if ("withCredentials" in xhr){ xhr.open(method, url, true); //IE瀏覽器 } else if (typeof XDomainRequest != "undefined"){ vxhr = new XDomainRequest(); xhr.open(method, url); } else { xhr = null; } return xhr; } var request = createCORSRequest("get", "http://www.somewhere-else.com/page/"); if (request){ request.onload = function(){ //對(duì)request.responseText 進(jìn)行處理 }; request.send(); }
(二) JSONP(JSON with Padding 填充式JSON 或參數(shù)式JSON)
在js中,我們雖然不能直接用XMLHttpRequest請(qǐng)求不同域上的數(shù)據(jù)時(shí),但是在頁(yè)面上引入不同域上的js腳本文件卻是可以的,jsonp正是利用這個(gè)特性來實(shí)現(xiàn)的
JSONP由兩部分組成:回調(diào)函數(shù)和數(shù)據(jù)?;卣{(diào)函數(shù)是當(dāng)響應(yīng)到來時(shí)應(yīng)該在頁(yè)面中調(diào)用的函數(shù),而數(shù)據(jù)就是傳入回調(diào)函數(shù)中的JSON數(shù)據(jù)。
例如:
<script type="text/javascript"> function dosomething(jsondata){ //處理獲得的json數(shù)據(jù) } </script> <script src="http://example.com/data.php?callback=dosomething"></script>
首先第一個(gè)script便簽定義了一個(gè)處理數(shù)據(jù)的函數(shù);
然后第二個(gè)script標(biāo)簽載入一個(gè)js文件,http://example.com/data.php 是數(shù)據(jù)所在地址,但是因?yàn)槭钱?dāng)做js來引入的,所以http://example.com/data.php 返回的必須是一個(gè)能執(zhí)行的js文件;
最后js文件載入成功后會(huì)執(zhí)行我們?cè)趗rl參數(shù)中指定的函數(shù),并且會(huì)把我們需要的json數(shù)據(jù)作為參數(shù)傳入。所以php應(yīng)該是這樣的
<?php $callback = $_GET['callback'];//得到回調(diào)函數(shù)名 $data = array('a','b','c');//要返回的數(shù)據(jù) echo $callback.'('.json_encode($data).')';//輸出 ?>
最終,輸出結(jié)果為:dosomething(['a','b','c']);
從上面可以看出jsonp是需要服務(wù)器端的頁(yè)面進(jìn)行相應(yīng)的配合的。
JSONP的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
它的兼容性更好,在更加古老的瀏覽器中都可以運(yùn)行,不需要XMLHttpRequest或ActiveX的支持;
能夠直接訪問響應(yīng)文本,支持在瀏覽器與服務(wù)器之間雙向通信
缺點(diǎn):
JSONP 是從其他域中加載代碼執(zhí)行。如果其他域不安全,很可能會(huì)在響應(yīng)中夾帶一些惡意代碼,而此時(shí)除了完全放棄JSONP 調(diào)用之外,沒有辦法追究。因此在使用不是你自己運(yùn)維的Web 服務(wù)時(shí),一定得保證它安全可靠。
它只支持GET請(qǐng)求而不支持POST等其它類型的HTTP請(qǐng)求;它只支持跨域HTTP請(qǐng)求這種情況,不能解決不同域的兩個(gè)頁(yè)面之間如何進(jìn)行JavaScript調(diào)用的問題。
(三) window.name
window對(duì)象有個(gè)name屬性,該屬性有個(gè)特征:即在一個(gè)窗口(window)的生命周期內(nèi),窗口載入的所有的頁(yè)面都是共享一個(gè)window.name的,每個(gè)頁(yè)面對(duì)window.name都有讀寫的權(quán)限,window.name是持久存在一個(gè)窗口載入過的所有頁(yè)面中的,并不會(huì)因新頁(yè)面的載入而進(jìn)行重置。
這里有三個(gè)頁(yè)面:
a.com/app.html:應(yīng)用頁(yè)面。
a.com/proxy.html:代理文件,一般是一個(gè)沒有任何內(nèi)容的html文件,需要和應(yīng)用頁(yè)面在同一域下。
b.com/data.html:應(yīng)用頁(yè)面需要獲取數(shù)據(jù)的頁(yè)面,可稱為數(shù)據(jù)頁(yè)面。
app.html
<iframe src="b.com/data.html" id="iframe"></iframe> <script> var iframe = document.getElementById("iframe"); iframe.src = "a.com/proxy.html";//這是一個(gè)與a.com/app.html同源的頁(yè)面 iframe.onload = function(){ var data = iframe.contentWindow.name; //取到數(shù)據(jù) } </script>
data.html
<script> // 這里是要傳輸?shù)臄?shù)據(jù),大小一般為2M,IE和firefox下可以大至32M左右 // 數(shù)據(jù)格式可以自定義,如json、字符串 window.name = "數(shù)據(jù)" </script>
iframe首先的地址是b.com/data.html,所以能取到window.name數(shù)據(jù);
但是iframe現(xiàn)在跟app.html并不同源,app.html無法獲取到數(shù)據(jù),所以又將iframe的鏈接跳轉(zhuǎn)至a.com/proxy.html這個(gè)代理頁(yè)面,現(xiàn)在app.html跟iframe就同源了。
注意:iframe由b.com/data.html跳轉(zhuǎn)到a.com/proxy.html頁(yè)面,window.name的value是不變的
獲取數(shù)據(jù)以后銷毀這個(gè)iframe,釋放內(nèi)存;這也保證了安全(不被其他域frame js訪問)
<script type="text/javascript"> iframe.contentWindow.document.write(''); iframe.contentWindow.close(); document.body.removeChild(iframe); </script>
(四) document.domain + iframe
對(duì)于主域相同而子域不同的例子,可以通過設(shè)置document.domain的辦法來解決。
具體的做法是可以在http://www.a.com/a.html 和http://script.a.com/b.html 兩個(gè)文件中分別設(shè)置document.domain = 'a.com',然后通過a.html文件中創(chuàng)建一個(gè)iframe,去控制iframe的contentDocument,這樣兩個(gè)js文件之間就可以“交互”了。
http://www.a.com/a.html頁(yè)面
<iframe src="http://script.a.com/b.html" frameborder="0"></iframe> <script> document.domain = 'a.com'; </script>
http://script.a.com/b.html頁(yè)面
<script> document.domain = 'a.com'; </script>
這樣倆個(gè)頁(yè)面就可以通過js相互訪問各種屬性和對(duì)象了。
document.domain的設(shè)置是有限制的,我們只能把document.domain設(shè)置成自身或更高一級(jí)的父域,且主域必須相同。例如:a.b.example.com 中某個(gè)文檔的document.domain 可以設(shè)成a.b.example.com、b.example.com 、example.com中的任意一個(gè),但是不可以設(shè)成 c.a.b.example.com,因?yàn)檫@是當(dāng)前域的子域,也不可以設(shè)成baidu.com,因?yàn)橹饔蛞呀?jīng)不相同了。
(五) HTML5的window.postMessage
window.postMessage(message,targetOrigin) 方法是html5新引進(jìn)的特性,可以使用它來向其它的window對(duì)象發(fā)送消息,無論這個(gè)window對(duì)象是屬于同源或不同源,目前IE8+、FireFox、Chrome、Opera等瀏覽器都已經(jīng)支持window.postMessage方法。
window.postMessage允許兩個(gè)窗口/幀之間跨域發(fā)送數(shù)據(jù)消息。從本質(zhì)上講,window.postMessage是一個(gè)跨域的無服務(wù)器墊片的Ajax。
用法:
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,“*”表示不作限制
數(shù)據(jù)發(fā)送端
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'; // 設(shè)定接收端的域,*則為不限制 ifr.contentWindow.postMessage('I was there!', targetOrigin); }; </script>
數(shù)據(jù)接收端
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); // 對(duì)a.com、index.html中window對(duì)象的引用 // 但由于同源策略,這里event.source不可以訪問window對(duì)象 } }, false); </script>
以上就是js實(shí)現(xiàn)跨域的多種方法,希望對(duì)大家的學(xué)習(xí)有所幫助。
相關(guān)文章
javascript之querySelector和querySelectorAll使用介紹
其實(shí)關(guān)于querySelector和querySelectorAll的介紹說明很多。在此主要是做個(gè)記錄2011-12-12詳談js中標(biāo)準(zhǔn)for循環(huán)與foreach(for in)的區(qū)別
下面小編就為大家?guī)硪黄斦刯s中標(biāo)準(zhǔn)for循環(huán)與foreach(for in)的區(qū)別。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-11-11JavaScript 替換Html標(biāo)簽實(shí)現(xiàn)代碼
這種技術(shù)被廣泛應(yīng)用于表單驗(yàn)證,語(yǔ)法高亮和危險(xiǎn)字符過濾中。一段話如果很長(zhǎng),如果不想像下面那樣替換,我們得想些辦法了。2009-10-10bootstrap表格內(nèi)容過長(zhǎng)時(shí)用省略號(hào)表示的解決方法
這篇文章主要介紹了bootstrap表格內(nèi)容過長(zhǎng)時(shí)用省略號(hào)表示的解決方法,需要的朋友可以參考下2017-11-11JS實(shí)現(xiàn)div內(nèi)部的文字或圖片自動(dòng)循環(huán)滾動(dòng)代碼
在某些情況下需要這樣的功能:使用JS實(shí)現(xiàn)div內(nèi)部的文字或圖片自動(dòng)循環(huán)滾動(dòng),接下來為大家詳細(xì)介紹下實(shí)現(xiàn)方法,感興趣的朋友可以參考下哈2013-04-04Bootstrap實(shí)現(xiàn)提示框和彈出框效果
這篇文章主要為大家詳細(xì)介紹了Bootstrap實(shí)現(xiàn)彈出框和提示框效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01