首頁 > web前端 > js教程 > 主體

JavaScript兩種跨域技術全面介紹_javascript技巧

WBOY
發布: 2016-05-16 16:52:10
原創
1302 人瀏覽過

這個策略對於JavaScript程式碼能夠存取的頁面內容做了很重要的限制,即JavaScript只能存取與包含它的文件在同一網域下的內容。

JavaScript這個安全策略在進行多iframe或多視窗程式設計、以及Ajax程式設計時顯得格外重要。根據這個策略,在baidu.com下的頁面中包含的JavaScript程式碼,不能存取在google.com網域下的頁面內容;甚至不同的子網域之間的頁面也不能透過JavaScript程式碼互相存取。對於Ajax的影響在於,透過XMLHttpRequest實現的Ajax請求,不能向不同的網域提交請求,例如,在abc.example.com下的頁面,不能向def.example.com提交Ajax請求,等等。

然而,當進行一些比較深入的前端編程的時候,不可避免地需要進行跨域操作,這時候「同源策略」就顯得過於苛刻。本文就這個問題,概括了跨域所需的一些技術。

下面我們分兩種情況討論跨域技術:首先討論不同子域的跨域技術,然後討論完全不同域的跨域技術。

(一)不同子域的跨域技術。

我們分成兩個問題分別討論:第一個問題是如何跨不同子域進行JavaScript呼叫;第二個問題是如何向不同子域提交Ajax請求。

先來解決第一個問題,假設example.com網域下有兩個不同子網域:abc.example.com和def.example.com。現在假設在def.example.com下面有一個頁面,裡面定義了一個JavaScript函數:

複製程式碼 程式碼如下:
function funcInDef() {
    .....
}

我們想要在abc.example.com下的某個頁面中呼叫上面的函數。再假設我們要討論的abc.example.com下面的這個頁面是以iframe形式嵌入在def.example.com下面那個頁面裡的,這樣我們可能試圖在iframe裡做如下調用:

複製程式碼 程式碼如下:
window.top.funcInDef();

好,我們注意到,這個呼叫是被前面講到的「同源策略」所禁止的,JavaScript引擎會直接拋出一個異常。
為了實現上述調用,我們可以透過修改兩個頁面的domain屬性的方法來做到。例如,我們可以將上面在abc.example.com和def.example.com下的兩個頁面的頂端都加上如下的JavaScript程式碼片段:
複製代碼 程式碼如下:
document.domain = "example.com";

這樣,兩個頁面就變成同域了,前面的呼叫也可以正常執行了。

這裡要注意的一點是,一個頁面的document.domain屬性只能設定成一個更頂級的網域(除了一級網域),但不能設定成比目前網域更深層的子網域。例如,abc.example.com的頁面只能將它的domain設定成example.com,不能設定成sub.abc.example.com,當然也不能設定成一級網域com。

上面的例子討論的是兩個頁面屬於iframe嵌套關係的情況,當兩個頁面是打開與被打開的關係時,原理也完全一樣。

下面我們來解決第二個問題:如何向不同子網域提交Ajax請求。
通常情況下,我們會用與下面類似的程式碼來建立一個XMLHttpRequest物件:

複製程式碼 程式碼如下:
factories = [function() {
    return new XMLHttpRequest();
},
function() {
  >},
function() {
    return new ActiveXObject("Microsoft.XMLHTTP");
}];
function newRequest() {
    for (var i = 0 i & lt; factories.length; i ) {
        try {
                     } catch(e) {}
    }
return null;
}

上面的程式碼中引用ActiveXObject,是為了相容於IE6系列瀏覽器。每次我們呼叫newRequest函數,就獲得了一個剛剛建立的Ajax對象,然後用這個Ajax物件來發送HTTP請求。例如,下面的程式碼向abc.example.com發送了一個GET請求:
複製程式碼 程式碼如下:

var request = newRequest();
request.open("GET", "http://abc.example.com" );
request.send(null);

假設上面的程式碼包含在一個abc.example.com網域下的頁面裡,則這個GET請求可以正常發送成功,沒有任何問題。然而,如果現在要向def.example.com發送請求,則出現跨域問題,JavaScript引擎拋出例外。
解決的方法是,在def.example.com域下放置一個跨域文件,假設叫crossdomain.html;然後將前面的newRequest函數的定義移到這個跨域文件中;最後像之前修改document. domain值的做法一樣,在crossdomain.html檔案和abc.example.com域下呼叫Ajax的頁面頂端,都加上:

複製程式碼

程式碼如下:

document.domain = "example.com";複製代碼

代碼如下:

複製程式碼

程式碼如下:

var request = window.frames["xd_Rerame"].new ();這樣取得的request對象,就可以向http://def.example.com發送HTTP請求了。


(二)完全不同域的跨域技術。


如果頂級網域都不相同,例如example1.com和example2.com之間想透過JavaScript在前端通信,則所需要的技術更複雜些。

在講解不同域的跨域技術之前,我們先明確一點,下面要講的技術也同樣適用於前面跨不同子域的情況,因為跨不同子域只是跨域問題的一個特例。當然,在適當的情況下使用適當的技術,能夠確保更優的效率和更高的穩定性。

簡言之,根據不同的跨域需求,跨域技術可以歸為下面幾類:
1、JSONP跨域GET請求
2、透過iframe實現跨域 3、flash跨域HTTP請求
4、window.postMessage
以下詳細介紹各種技術。

1. JSONP。

利用在頁面中建立<script>節點的方法向不同網域提交HTTP請求的方法稱為JSONP,這項技術可以解決跨域提交Ajax請求的問題。 JSONP的工作原理如下所述:<a style="CURSOR: pointer" data="91817" class="copybut" id="copybut91817" onclick="doCopy('code91817')">假設在http://example1.com/index.php這個頁面中向http://example2.com/getinfo.php提交GET請求,我們可以將下面的JavaScript程式碼放在http://example1.com/index.php這個頁面來實作:<U></script>
複製程式碼


程式碼如下:
var eleScript= document.createElement("script");eleScript.type = "text/javascript";eleScript.src = "http://example2.com/getinfo.php"; document.getElementsByTagName("HEAD")[0].appendChild(eleScript);

当GET请求从http://example2.com/getinfo.php返回时,可以返回一段JavaScript代码,这段代码会自动执行,可以用来负责调用http://example1.com/index.php页面中的一个callback函数。

JSONP的优点是:它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制;它的兼容性更好,在更加古老的浏览器中都可以运行,不需要XMLHttpRequest或ActiveX的支持;并且在请求完毕后可以通过调用callback的方式回传结果。

JSONP的缺点则是:它只支持GET请求而不支持POST等其它类型的HTTP请求;它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。

2. 通过iframe实现跨域。

iframe跨域的方式,功能强于JSONP,它不仅能用来跨域完成HTTP请求,还能在前端跨域实现JavaScript调用。因此,完全不同域的跨域问题,通常采用iframe的方式来解决。

与JSONP技术通过创建<script>节点向不同的域提交GET请求的工作方式类似,我们也可以通过在http://example1.com/index.php页面中创建指向http://example2.com/getinfo.php的iframe节点跨域提交GET请求。然而,请求返回的结果无法回调http://example1.com/index.php页面中的callback函数,因为受到“同源策略”的影响。<br><br>为了解决这个问题,我们需要在example1.com下放置一个跨域文件,比如路径是http://example1.com/crossdomain.html。<br><br>当http://example2.com/getinfo.php这个请求返回结果的时候,它大体上有两个选择。<br>第一个选择是,它可以在iframe中做一个302跳转,跳转到跨域文件http://example1.com/crossdomain.html,同时将返回结果经过URL编码之后作为参数缀在跨域文件URL后面,例如http://example1.com/crossdomain.html?result=<URL-Encoding-Content>。<br><br>另一个选择是,它可以在返回的页面中再嵌入一个iframe,指向跨域文件,同时也是将返回结果经过URL编码之后作为参数缀在跨域文件URL后面。<br><br>在跨域文件中,包含一段JavaScript代码,这段代码完成的功能,是从URL中提取结果参数,经过一定处理后调用原来的http://example1.com/index.php页面中的一个预先约定好的callback函数,同时将结果参数传给这个函数。http://example1.com/index.php页面和跨域文件是在同一个域下的,因此这个函数调用可以通过。跨域文件所在iframe和原来的http://example1.com/index.php页面的关系,在前述第一种选择下,后者是前者的父窗口,在第二种选择下,后者是前者的父窗口的父窗口。<br><br>根据前面的叙述,有了跨域文件之后,我们就可以实现通过iframe方式在不同域之间进行JavaScript调用。这个调用过程可以完全跟HTTP请求无关,例如有些站点可以支持动态地调整在页面中嵌入的第三方iframe的高度,这其实是通过在第三方iframe里面检测自己页面的高度变化,然后通过跨域方式的函数调用将这个变化告知父窗口来完成的。<br><br>既然利用iframe可以实现跨域JavaScript调用,那么跨域提交POST请求等其它类型的HTTP请求就不是难事。例如我们可以跨域调用目标域的JavaScript代码在目标域下提交Ajax请求(GET/POST/etc.),然后将返回的结果再跨域传原来的域。<br><br>使用iframe跨域,优点是功能强大,支持各种浏览器,几乎可以完成任何跨域想做的事情;缺点是实现复杂,要处理很多浏览器兼容问题,并且传输的数据不宜过大,过大了可能会超过浏览器对URL长度的限制,要考虑对数据进行分段传输等。</p> <p><strong>3. 利用flash实现跨域HTTP请求<br></strong><br>据称,flash在浏览器中的普及率高达90%以上。<br><br>flash代码和JavaScript代码之间可以互相调用,并且flash的“安全沙箱”机制与JavaScript的安全机制并不尽相同,因此,我们可以利用flash来实现跨域提交HTTP请求(支持GET/POST等)。<br>例如,我们用浏览器访问http://example1.com/index.php这个页面,在这个页面中引用了http://example2.com/flash.swf这个flash文件,然后在flash代码中向http://example3.com/webservice.php发送HTTP请求。<br><br>这个请求能否被成功发送,取决于在example3.com的根路径下是否放置了一个crossdomain.xml以及这个crossdomain.xml的配置如何。flash的“安全沙箱”会保证:仅当example3.com服务器在根路径下确实放置了crossdomain.xml文件并且在这个文件中配置了允许接受来自example2.com的flash的请求时,这个请求才能真正成功。下面是一个crossdomain.xml文件内容的例子:</p> <p></p> <div class="codetitle"> <span><a style="CURSOR: pointer" data="91153" class="copybut" id="copybut91153" onclick="doCopy('code91153')"><u>複製程式碼</u></a></span> 程式碼如下:</div> <div class="codebody" id="code91153"> <br><?xml version="1.0" ><cross-domain-policy><br>    <allow-access-from domain="example2.com"></allow-access-from><br></cross-domain-policy><br><br> </div> <p>4. window.postMessage<strong><br>  window.postMessage是HTML標準的下一個版本HTML5支援的新功能。受目前網路技術突飛猛進的影響,瀏覽器跨域通訊的需求越來越強烈,HTML標準終於把跨域通訊考慮進去了。但目前HTML5仍然只是一個draft。 </strong>  window.postMessage是一個安全的實作直接跨域通訊的方法。但目前並不是所有瀏覽器都能支持,只有Firefox 3、Safari 4和IE8可以支援這個呼叫。 <br><br>使用它向其它視窗發送訊息的呼叫方式大概如下:<br><br></p> <div class="codetitle"> <span><a style="CURSOR: pointer" data="66586" class="copybut" id="copybut66586" onclick="doCopy('code66586')">複製程式碼<u></u></a></span>複製程式碼</div> <div class="codebody" id="code66586"></div> 程式碼<br><br>otherWindow.postMessage(message, targetOrigin);<div class="codetitle"> <span>在接收的窗口,需要設定一個事件處理函數來接收發過來的訊息:<a style="CURSOR: pointer" data="40287" class="copybut" id="copybut40287" onclick="doCopy('code40287')"><u></u></a></span>複製程式碼</div> <div class="codebody" id="code40287"> <br> 程式碼如下:<br><br>window.addEventListener("message", receiveMessage, false);}<br><br>訊息包含三個屬性:data、origin(攜帶發送視窗所在網域的真實資訊)和source (代表發送視窗的handle)。 <br><br>安全性考量:使用window.postMessage,必需要使用訊息的origin和source屬性來驗證傳送者的身份,否則會造成XSS漏洞。 <br><br>window.postMessage在功能上同iframe實現的跨域功能同樣強大,並且使用簡單,效率更高,但缺點是它目前在瀏覽器兼容方面有待提高。 <br><br>需要對原文補充的是,在IE6,IE7下可利用IE的Opener可賦值為Object或Function的漏洞,提供postMessage方案的補充方案:主頁:<p> </p> <div class="codetitle"><span><a style="CURSOR: pointer" data="92626" class="copybut" id="copybut92626" onclick="doCopy('code92626')"><u></u>複製程式碼</a></span></div> 程式碼如下:<div class="codebody" id="code92626"> <br> <br><br><br>    <title>跨域</title> <br>&lt ;/head><br><br>    <iframe src="http://sh-tanzhenlin/CrossDomain-child.html">       frameborder="0"visible="false"height=" 0" width="0"visible="false"height="0" width="0"visible="false"height="0" width="0" id="ifrChild"></iframe><br><br>    <script type="text/javascript"><BR>        var child = document.getElement >                funcInParent:function(arg){<BR>                   alert('由父親頁面中的函數執行') ;<BR>                }<BR> }<🠎 v1' && !'1'[0]){ //測試瀏覽器是ie6或ie7   <BR>            //破解<BR>     <BR>        else{<br> //postMessage showtime<br>        }<BR><BR>        function onClick(){<BR>     openerObject.funcInIframe('來自父親頁面的資料') ;<BR>        }<BR> </script>
   





用iframe內嵌其他網域下的頁面:



複製程式碼



程式碼如下:






                      onload = function(){
                                              地alert(arg);
                                              alert('由函數執行在iframe 中');
                                        }
                                                window.opener.funcInParent('來自iframe 的數據')
                               }
                       }
               
       
       >
註:postMessage 方式正考慮以方便的速度得到各種新瀏覽器的支持,應可接受的商品。
相關標籤:
來源:php.cn
上一篇:JavaScript數組深拷貝和淺拷貝的兩種方法_javascript技巧 下一篇:JavaScript事件委託的技術原理探討範例_javascript技巧
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
最新問題
相關專題
更多>
熱門推薦
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!