文章出處

前面的話

  script、image、iframe的src都不受同源策略的影響。所以可以借助這一特點,實現跨域。例如,前面介紹的jsonp是使用script標簽,imgPing是使用image標簽,而本文將介紹使用iframe標簽實現跨域

 

引入

  1995年,同源政策由 Netscape 公司引入瀏覽器。目前,所有瀏覽器都實行這個政策。最初,它的含義是指,A 網頁設置的 Cookie,B 網頁不能打開,除非這兩個網頁“同源”。所謂“同源”指的是”三個相同“:1、協議相同;2、域名相同;3、端口相同

  舉例來說,http://www.example.com/dir/page.html這個網址,協議是http://,域名是www.example.com,端口是80(默認端口可以省略)。它的同源情況如下

http://www.example.com/dir2/other.html:同源
http://example.com/dir/other.html:不同源(域名不同)
http://v2.www.example.com/dir/other.html:不同源(域名不同)
http://www.example.com:81/dir/other.html:不同源(端口不同)
https://www.example.com/dir/page.html:不同源(協議不同)

  同源政策的目的,是為了保證用戶信息的安全,防止惡意的網站竊取數據。

  設想這樣一種情況:A 網站是一家銀行,用戶登錄以后,又去瀏覽其他網站。如果其他網站可以讀取 A 網站的 Cookie,會發生什么?很顯然,如果 Cookie 包含隱私(比如存款總額),這些信息就會泄漏。更可怕的是,Cookie 往往用來保存用戶的登錄狀態,如果用戶沒有退出登錄,其他網站就可以冒充用戶,為所欲為。因為瀏覽器同時還規定,提交表單不受同源政策的限制。

  由此可見,“同源政策”是必需的,否則 Cookie 可以共享,互聯網就毫無安全可言了

  隨著互聯網的發展,“同源政策”越來越嚴格。目前,如果非同源,共有三種行為受到限制

  1、Cookie、LocalStorage 和 IndexedDB 無法讀取

  2、DOM 無法獲得

  3、AJAX 請求無效(可以發送,但瀏覽器會拒絕接受響應)

  雖然這些限制是必要的,但是有時很不方便,合理的用途也受到影響

 

iframe

  iframe元素可以在當前網頁之中,嵌入其他網頁。每個iframe元素形成自己的窗口,即有自己的window對象。iframe窗口之中的腳本,可以獲得父窗口和子窗口。但是,只有在同源的情況下,父窗口和子窗口才能通信;如果跨域,就無法拿到對方的DOM

  [注意]關于iframe的詳細信息移步至此

  比如,父窗口和子窗口的代碼如下所示,都處于localhost域下

<!-- 父窗口test.html-->
<body>
  <iframe id="myIFrame" src="iframe.html"></iframe>
  <script>
    var iframe = document.getElementById("myIFrame");
    iframe.onload = function(){
      var doc = iframe.contentWindow.document;
      console.log(doc.getElementById('test').innerHTML);//'xiaohuochai'
      console.log(document.cookie);//'name=match'
    }
  </script>
</body>
<!-- 子窗口iframe.html-->
<body>
  <div id="test">xiaohuochai</div>
  <script>
  document.cookie = 'name=match';
  </script>
</body>

  如果iframe窗口不是同源,如處于文件域下(file:///C:/Users/Administrator/Desktop/demo/js/test.html),就會報錯

  <iframe id="myIFrame" src="iframe.html"></iframe>
  <script>
    var iframe = document.getElementById("myIFrame");
    iframe.onload = function(){
    //Uncaught DOMException: Blocked a frame with origin "null" from accessing a cross-origin frame. console.log(iframe.contentWindow.document); } </script>

  上面命令中,父窗口想獲取子窗口的DOM,因為跨域導致報錯。

  反之亦然,子窗口獲取主窗口的DOM也會報錯。

window.parent.document.body
// 報錯

  這種情況不僅適用于iframe窗口,還適用于window.open方法打開的窗口,只要跨域,父窗口與子窗口之間就無法通信

 

domain屬性

  如果兩個窗口一級域名相同,只是二級域名不同,可以通過設置document.domain來使其通信

  父窗口地址為https://static.xiaohuochai.site/test/test.html

  子窗口地址為https://demo.xiaohuochai.site/test/iframe.html

  代碼如下

<!-- 父窗口test.html-->
<body>
  <iframe id="myIFrame" src="https://demo.xiaohuochai.site/test/iframe.html"></iframe>
  <script>
    var iframe = document.getElementById("myIFrame");
    iframe.onload = function(){
      var doc = iframe.contentWindow.document;
      console.log(doc.getElementById('test').innerHTML);//'xiaohuochai'
      console.log(document.cookie);
    }
  </script>
</body>
<!-- 子窗口iframe.html-->
<body>
  <div id="test">xiaohuochai</div>
  <script>
  document.cookie = 'name=match';
  </script>
</body>

  由結果所示,通過設置document.domain只能獲取DOM,而Cookie、LocalStorage 和 IndexedDB 無法讀取

 

錨點值

  錨點值,又稱為片段標識符(fragment identifier),指的是URL的#號后面的部分,比如http://example.com/x.html#fragment#fragment。如果只是改變片段標識符,頁面不會重新刷新

  父窗口可以把信息,寫入子窗口的錨點值

var src = originURL + '#' + data;
document.getElementById('myIFrame').src = src;

  子窗口通過監聽hashchange事件得到通知

window.onhashchange = checkMessage;

function checkMessage() {
  var message = window.location.hash;
  // ...
}

  同樣的,子窗口也可以改變父窗口的片段標識符

parent.location.href= target + '#' + hash;

  下面是具體代碼

<!-- 父窗口test.html-->
<body>
  <iframe id="myIFrame" src="iframe.html"></iframe>
  <script>
    var iframe = document.getElementById("myIFrame");
    window.onhashchange = function (e) {
      console.log(/.*#(.*)/g.exec(e.newURL)[1])//'xiaohuochai'  
    } 
  </script>
</body>
<!-- 子窗口iframe.html-->
<body>
  <div id="test">xiaohuochai</div>
  <script>
    parent.location.href = 'test.html' + '#' + test.innerHTML;
  </script>
</body>

 

XDM

  上面兩種方法都屬于破解,HTML5為了解決這個問題,引入了一個全新的API:跨文檔通信 API

  [注意]IE8-瀏覽器不支持

  跨文檔消息傳送(cross-document messaging),有時候簡稱為XDM,指的是在來自不同域的頁面間傳遞消息。例如,www.wrox.com域中的頁面與位于一個內嵌框架中的p2p.wrox.com域中的頁面通信。 在XDM機制出現之前,要穩妥地實現這種通信需要花很多工夫。XDM把這種機制規范化,讓我們能既穩妥又簡單地實現跨文檔通信

  XDM的核心是postMessage ()方法。在HTML5規范中,除了 XDM部分之外的其他部分也會提到這個方法名,但都是為了同一個目的:向另一個地方傳遞數據。對于XDM而言,“另一個地方”指的是包含在當前頁面中的<iframe>元素,或者由當前頁面彈出的窗口

  postMessage()方法接收兩個參數:一條消息和一個表示消息接收方來自哪個域的字符串。第二個參數對保障安全通信非常重要,可以防止瀏覽器把消息發送到不安全的地方

  來看下面的例子。

//注意:所有支持XDM的瀏覽器也支持ifraaie的contentWindow屬性
var iframeWindow = document.getElementById("rayframe").contentWindow,
iframeWindow.postMessage( "A secret', "http://www.wrox.com");

  最后一行代碼嘗試向內嵌框架中發送一條消息,并指定框架中的文檔必須來源于"http://www.wrox.com"域。如果來源匹配,消息會傳遞到內嵌框架中;否則,postMessage()什么也不做。 這一限制可以避免窗口中的位置在你不知情的情況下發生改變。如果傳給postMessage()的第二個參數是"*",則表示可以把消息發送給來自任何域的文檔,但不推薦這樣做

  接收到XDM消息時,會觸發window對象的message事件。這個事件是以異步形式觸發的,因此從發送消息到接收消息(觸發接收窗口的message事件)可能要經過一段時間的延遲。觸發message事件后,傳遞給onmessage處理程序的事件對象包含以下三方面的重要信息

data:作為postMessage()第一個參數傳入的字符串數據
origin:發送消息的文檔所在的域,例如"http://www.wrox.com"。 
source:發送消息的文檔的window對象的代理。這個代理對象主要用于在發送上一條消息的窗口中調用postMessage()方法。如果發送消息的窗口來自同一個域,那這個對象就是window

  接收到消息后驗證發送窗口的來源是至關重要的。就像給postMessage()方法指定第二個參數, 以確保瀏覽器不會把消息發送給未知頁面一樣,在onmessage處理程序中檢測消息來源可以確保傳入的消息來自已知的頁面。基本的檢測模式如下

window.onmessage = function(e){
  if(e.origin == 'http://www.wrox.com'){
    //處理接收到的數據
    processMessage(e.data);
    //可選:向來源窗口發送回執
    e.source.postMessage("Received!", "http//p2p.wrox.com");
  }
}

  [注意]event.source大多數情況下只是window對象的代理,并非實際的window對象。換句話說,不能通過這個代理對象訪問window對象的其他任何信息。只通過這個代理調用 postMessage()就好,這個方法永遠存在,永遠可以調用

  XDM還有一些怪異之處

  postMessage()的第一個參數最早是作為“永遠都是字符串”來實現的。但后來這個參數的定義改了,改成允許傳入任何數據結構。可是,并非所有瀏覽器都實現了這一變化。為保險起見,使用postMessage()時,最好還是只傳字符串。如果想傳入結構化的數據,最佳選擇是先在要傳入的數據上調用JSON.stringify(),通過postMessage()傳入得到的字符串,然 后再在onmessage事件處理程序中調用JSON.parse()

  在通過內嵌框架加載其他域的內容時,使用XDM是非常方便的。因此,在混搭(mashup)和社交網絡應用中,這種傳遞消息的方法極為常用。有了XDM,包含<iframe>的頁面可以確保自身不受惡意內容的侵擾,因為它只通過XDM與嵌入的框架通信。而XDM也可以在來自相同域的頁面間使用

  下面是一個實例

  父窗口地址為https://static.xiaohuochai.site/test/test_1.html

  子窗口地址為https://demo.xiaohuochai.site/test/iframe_1.html

  代碼如下

<!-- 父窗口test_1.html-->
<body>
  <iframe id="myIFrame" src="https://demo.xiaohuochai.site/test/iframe_1.html"></iframe>
  <script>
    window.onmessage = function (e) {
      if(e.origin === 'https://demo.xiaohuochai.site'){
        console.log(e.data);//'xiaohuochai'
      }
    }
  </script>
</body>
<!-- 子窗口iframe_1.html-->
<body>
  <script>
  if (window.parent !== window.self) {
    window.parent.postMessage('xiaohuochai', 'https://static.xiaohuochai.site');
  }
  </script>
</body>

 


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()