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