溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Iframe跨窗口通信原理是什么

發(fā)布時(shí)間:2022-08-05 10:51:35 來(lái)源:億速云 閱讀:216 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要講解了“Iframe跨窗口通信原理是什么”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“Iframe跨窗口通信原理是什么”吧!

同源

同源策略會(huì)限制 窗口(window) 和 frame 之間的通信,因此首先要知道同源策略。

同源策略目的是保護(hù)用戶信息免遭信息盜竊:加入小王有兩個(gè)打開的頁(yè)面:一個(gè)是 shop.com,一個(gè)是 email.com。小王不希望 shop.com 的腳本可以讀取 mail 的郵件,這時(shí)同源策略就起作用了。

如果兩個(gè) URL 具有相同的協(xié)議,域,和端口,則稱它們是同源的。

以下幾個(gè)URL是同源的:

  • site.com

  • site.com/

  • site.com/a/index.htm…

以下是不同源的:

  • https://site.com

  • http://bbs.site.com

  • site.com:8080

  • http://site.org

同源策略規(guī)定:

  • 如果我們有對(duì)另一個(gè)窗口的引用(window.open || iframe),并且該窗口是同源的,那么我們就具有對(duì)該窗口的全部訪問權(quán)限。見代碼:2-1 、2-2

  • 如果不是同源的,我們就不能訪問窗口中的內(nèi)容:變量,文檔,任何東西。唯一例外是location:我們可以修改它,使用它進(jìn)行重定向。但是我們無(wú)法讀取 location 。因此,我們無(wú)法看到用戶當(dāng)前所處的位置,也就不會(huì)泄露任何信息。

iframe

iframe 標(biāo)簽承載了一個(gè)單獨(dú)的嵌入的窗口,它有自己的 documentwindow

iframe.contentWindow 來(lái)獲取 中的 window

iframe.contentDocument 來(lái)獲取 中的 document , 是 iframe.contentWindow.document 的簡(jiǎn)寫。

當(dāng)我們?cè)L問嵌入的窗口中的東西時(shí),瀏覽器會(huì)檢查 iframe 是否具有相同的源。如果不是,則會(huì)拒絕訪問(對(duì) location 進(jìn)行寫入是一個(gè)例外,它是會(huì)被允許的)。

代碼 2-1 : (在 同源 情況下)

 <!-- 1.html 內(nèi)容 -->
 <!-- http://127.0.0.1:8000/1.html -->
 <body>
     我是 1.html, 下面嵌套 2.html
     <iframe src="http://127.0.0.1:8000/2.html" ></iframe>
     <script>
         function hello () { console.log('this is 1.html') }
         var iframe = document.getElementsByTagName('iframe')[0];
         console.log('contentWindow ????', iframe.contentWindow); // 能訪問
         console.log('contentDocument ????', iframe.contentDocument); // 能訪問
         // 注意訪問方式, 需要在 onload 后才能取到值
         console.log( iframe.contentWindow.hello() ) // Uncaught TypeError: iframe.contentWindow.hello is not a function
         iframe.onload = function(){
             console.log( iframe.contentWindow.hello() ) // this is 2.html
             //  輸出 Location 對(duì)象, 依然要在 iframe.onload 中訪問
             console.log('contentWindow.location ????', iframe.contentWindow.location)
             //  iframe.contentWindow.location.host : 127.0.0.1:8000
             //  xxx.hash:     ""
             //  xxx.host:     "127.0.0.1:8000"
             //  xxx.hostname: "127.0.0.1"
             //  xxx.href:     "http://127.0.0.1:8000/2.html"
             //  xxx.origin:   "http://127.0.0.1:8000"
             //  xxx.pathname: "/2.html"
             //  xxx.port:     "8000"    
             //  xxx.protocol: "http:"
             //  ...
             // 有相同的源 我們可以進(jìn)行任何操作
             iframe.contentDocument.body.innerHTML('<p>hi, i am ur father !</p>');
             iframe.contentDocument.getElementsByTagName('p');
         })
         iframe.contentWindow.location = 'http://www.360doc.com'; // 可以直接修改 iframe 地址, 不受同源策略的限制。 有的網(wǎng)站不支持被iframe引用, 所以會(huì)報(bào)錯(cuò)。 注意區(qū)分錯(cuò)誤信息。
     </script>
 </body>
 <!-- 2.html 內(nèi)容 -->
 <!-- http://127.0.0.1:8000/2.html -->
 <body>
     我是 2.html
     <script>
         function hello () { console.log('this is 2.html') }
     </script>
 </body>

iframe.onload vs iframe.contentWindow.onload

iframe.onload 事件(在 <iframe> 標(biāo)簽上)與 iframe.contentWindow.onload(在嵌入的 window 對(duì)象上)基本相同。當(dāng)嵌入的窗口的所有資源都完全加載完畢時(shí)觸發(fā)。

&hellip;&hellip;但是,我們無(wú)法使用 iframe.contentWindow.onload 訪問不同源的 iframe。因此,請(qǐng)使用 iframe.onload。

window:document.domain

但是,如果窗口的二級(jí)域相同,例如 bbs.site.com,nav.site.comsite.com(它們共同的二級(jí)域是 site.com),我們可以使瀏覽器忽略該差異,使得它們可以被作為“同源”的來(lái)對(duì)待,以便進(jìn)行跨窗口通信。

為了做到這一點(diǎn),每個(gè)這樣的窗口都應(yīng)該執(zhí)行下面這行代碼:

 document.domain = 'site.com';

這樣就可以了?,F(xiàn)在它們可以無(wú)限制地進(jìn)行交互了。但是再?gòu)?qiáng)調(diào)一遍,這僅適用于具有相同二級(jí)域的頁(yè)面。

已棄用,但仍有效

document.domain 屬性正在被從 規(guī)范 中刪除??绱翱谕ㄐ牛ㄏ旅鎸⒑芸旖忉尩剑┦墙ㄗh的替代方案。

也就是說,到目前為止,所有瀏覽器都支持它。并且未來(lái)也將繼續(xù)支持它,而不會(huì)導(dǎo)致使用了 document.domain 的舊代碼出現(xiàn)問題。

代碼 2-1 : (在 不同源 情況下)注意端口

 <!-- 1.html 內(nèi)容 -->
 <!-- http://127.0.0.1:8000/1.html -->
 <body>
     我是 1.html, 下面嵌套 2.html
     <!-- 端口不同, 不同源 -->
     <iframe src="http://127.0.0.1:8001/2.html" ></iframe>
     <script>
         function hello () { console.log('this is 1.html') }
         var iframe = document.getElementsByTagName('iframe')[0];
         console.log('contentWindow ????', iframe.contentWindow); // 可以獲取對(duì)內(nèi)部 window 的引用
         console.log('contentDocument ????', iframe.contentDocument); // 空的 document 對(duì)象
         iframe.onload = function(){
             console.log( iframe.contentWindow.hello() ) // Uncaught DOMException: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame.
             // 無(wú)法讀取 iframe 中頁(yè)面的 URL
             console.log( iframe.contentWindow.location )
             // Location {then: undefined, Symbol(Symbol.toStringTag): undefined, Symbol(Symbol.hasInstance): undefined, Symbol(Symbol.isConcatSpreadable): undefined, replace: ?}
             console.log(iframe.contentWindow.location.href) // Uncaught DOMException: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. 
         })
         iframe.contentWindow.location = 'http://www.360doc.com'; // 可以直接修改 iframe 地址, 不受同源策略的限制,不受同源策略的限制,不受同源策略的限制。 
     </script>
 </body>
 <!-- 2.html 內(nèi)容 -->
 <!-- http://127.0.0.1:8001/2.html -->
 <body>
     我是 2.html
     <script>
         function hello () { console.log('this is 2.html') }
     </script>
 </body>

iframe:錯(cuò)誤文檔陷阱

當(dāng)一個(gè) iframe 來(lái)自同一個(gè)源時(shí),我們可能會(huì)訪問其 document,但是這里有一個(gè)陷阱。它與跨源無(wú)關(guān),但你一定要知道。在創(chuàng)建 iframe 后,iframe 會(huì)立即就擁有了一個(gè)文檔。但是該文檔不同于加載到其中的文檔!

因此,如果我們要立即對(duì)文檔進(jìn)行操作,就可能出問題,因?yàn)槟鞘?strong>錯(cuò)誤的文檔。

正確的文檔在 iframe.onload 觸發(fā)時(shí)肯定就位了。但是,只有在整個(gè) iframe 和它所有資源都加載完成時(shí),iframe.onload 才會(huì)觸發(fā)。

看一下下面這段代碼:

 let oldDoc = iframe.contentDocument;
 iframe.onload = function() {
     let newDoc = iframe.contentDocument;
     // 加載的文檔與初始的文檔不同!
     alert(oldDoc == newDoc); // false
 };

window.frames

獲取 <iframe> 的 window 對(duì)象的另一個(gè)方式是從命名集合 window.frames 中獲?。?/p>

  • 通過索引獲?。?code>window.frames[0] &mdash;&mdash; 文檔中的第一個(gè) iframe 的 window 對(duì)象。

  • 通過名稱獲?。?code>window.frames.iframeName &mdash;&mdash; 獲取 name="iframeName" 的 iframe 的 window 對(duì)象。

例如:

 // <iframe src="/"  name="win" id="iframe"></iframe>
 alert(iframe.contentWindow == frames[0]); // true
 alert(iframe.contentWindow == frames.win); // true

一個(gè) iframe 內(nèi)可能嵌套了其他的 iframe。相應(yīng)的 window 對(duì)象會(huì)形成一個(gè)層次結(jié)構(gòu)(hierarchy)。

可以通過以下方式獲?。?/p>

window.frames &mdash;&mdash; “子”窗口的集合(用于嵌套的 iframe)。

window.parent &mdash;&mdash; 對(duì)“父”(外部)窗口的引用。

window.top &mdash;&mdash; 對(duì)最頂級(jí)父窗口的引用。

例如:

 window.frames[0].parent === window; // true

我們可以使用 top 屬性來(lái)檢查當(dāng)前的文檔是否是在 iframe 內(nèi)打開的:

 if (window === window.top) { 
   alert('不是在 iframe 中打開的');
 } else {
   alert('在 iframe 中打開的');
 }

“sandbox” iframe 特性

sandbox 特性(attribute)允許在 <iframe> 中禁止某些特定行為,以防止其執(zhí)行不被信任的代碼。它通過將 iframe 視為非同源的,或者應(yīng)用其他限制來(lái)實(shí)現(xiàn) iframe 的“沙盒化”。

對(duì)于 <iframe sandbox src="...">,有一個(gè)應(yīng)用于其上的默認(rèn)的限制集。但是,我們可以通過提供一個(gè)以空格分隔的限制列表作為特性的值,來(lái)放寬這些限制,該列表中的各項(xiàng)為不應(yīng)該應(yīng)用于這個(gè) iframe 的限制,例如:<iframe sandbox="allow-forms allow-popups">

換句話說,一個(gè)空的 "sandbox" 特性會(huì)施加最嚴(yán)格的限制,但是我們用一個(gè)以空格分隔的列表,列出要移除的限制。

以下是限制的列表:

allow-same-origin

默認(rèn)情況下,"sandbox" 會(huì)為 iframe 強(qiáng)制實(shí)施“不同來(lái)源”的策略。換句話說,它使瀏覽器將 iframe 視為來(lái)自另一個(gè)源,即使其 src 指向的是同一個(gè)網(wǎng)站也是如此。具有所有隱含的腳本限制。此選項(xiàng)會(huì)移除這些限制。

allow-top-navigation

允許 iframe 更改 parent.location。

allow-forms

允許在 iframe 中提交表單。

allow-scripts

允許在 iframe 中運(yùn)行腳本。

allow-popups

允許在 iframe 中使用 window.open 打開彈窗。

查看 官方手冊(cè) 獲取更多內(nèi)容。

iframe 通信:postMessage onmessage

postMessage 接口允許窗口之間相互通信,無(wú)論它們來(lái)自什么源。

因此,這是解決“同源”策略的方式之一。它允許來(lái)自于 marh.com 的窗口與來(lái)自于 qq.com 的窗口進(jìn)行通信,并交換信息,但前提是它們雙方必須均同意并調(diào)用相應(yīng)的 JavaScript 函數(shù)。這可以保護(hù)用戶的安全。

這個(gè)接口有兩個(gè)部分。

1 postMessage

想要發(fā)送消息的窗口需要調(diào)用接收窗口的 postMessage 方法。換句話說,如果我們想把消息發(fā)送給 win,我們應(yīng)該調(diào)用 win.postMessage(data, targetOrigin)

參數(shù)

data

要發(fā)送的數(shù)據(jù)??梢允侨魏螌?duì)象,數(shù)據(jù)會(huì)被通過使用“結(jié)構(gòu)化序列化算法(structured serialization algorithm)”進(jìn)行克隆。IE 瀏覽器只支持字符串,因此我們需要對(duì)復(fù)雜的對(duì)象調(diào)用 JSON.stringify 方法進(jìn)行處理,以支持該瀏覽器。

targetOrigin

指定目標(biāo)窗口的源,以便只有來(lái)自給定的源的窗口才能獲得該消息。

 // <iframe src="http://127.0.0.1:8080/2.html" name="example" />  
 let win = window.frames.example;    
 win.postMessage("message", "http://127.0.0.1:8080");

2 onmessage

為了接收消息,目標(biāo)窗口應(yīng)該在 message 事件上有一個(gè)處理程序。當(dāng) postMessage 被調(diào)用時(shí)觸發(fā)該事件(并且 targetOrigin 檢查成功)。

event 對(duì)象具有特殊屬性:

datapostMessage 傳遞來(lái)的數(shù)據(jù)。

origin 發(fā)送方的源,例如 http://javascript.info。

source 對(duì)發(fā)送方窗口的引用。如果我們想,我們可以立即 source.postMessage(...) 回去。

要為 message 事件分配處理程序,我們應(yīng)該使用 addEventListener,簡(jiǎn)短的語(yǔ)法 window.onmessage 不起作用。

 window.addEventListener("message", function(event) {
   console.log(event)
   if (event.origin != 'http://http://127.0.0.1:8080') {
     // 來(lái)自未知的源的內(nèi)容,我們忽略它
     return;
   }
   if (window == event.source) {
     // chrome 下, 頁(yè)面初次加載后會(huì)觸發(fā)一次 message 事件, event.source 是 window 對(duì)象
     // 此時(shí) event.source.postMessage 會(huì)形成死循環(huán)
     // 因此,要跳過第一次的初始化觸發(fā)的情況
     return
   }
   console.log( "received: " + event.data );
   // 可以使用 event.source.postMessage(...) 向回發(fā)送消息
   event.source.postMessage('i am 2.html')
 }, source);

跨窗口的 cookie

 <!-- 1.html 內(nèi)容 -->
 <!-- http://127.0.0.1:8000/1.html -->
 <body>
     我是 1.html, 下面嵌套 2.html
     <!-- 端口不同, 不同源 -->
     <iframe src="http://127.0.0.1:8001/2.html" ></iframe>
     <script>
         if (!document) {
             document.cookie = 'name=1';
             document.cookie = 'old=10';    
         }
         console.log('1.html', document.cookie)
     </script>
 </body>
 <!-- 2.html 內(nèi)容 -->
 <!-- http://127.0.0.1:8000/2.html -->
 <body>
     我是 2.html
     <script>
         document.cookie = 'name=2';
         document.cookie = 'year=2020';
         console.log('2.html', document.cookie)
     </script>
 </body>

第一次渲染輸出:

1.html name=1; old=10 ,

2.html old=10; name=2; year=2020

刷新頁(yè)面輸出:

1.html old=10; year=2020; name=2 ,

2.html old=10; year=2020; name=2

我們可以得出以下結(jié)論:

  • iframe 嵌套的 2.html 設(shè)置的 cookie 我們可以從 1.html 中獲取

  • iframe 中設(shè)置的 cookie 會(huì)覆蓋 1.html cookie 中 Name相同的值( 不同源也是同樣的效果 )

感謝各位的閱讀,以上就是“Iframe跨窗口通信原理是什么”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對(duì)Iframe跨窗口通信原理是什么這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI