【知識點】關於iframe跨域通信

語言: CN / TW / HK
ead>

theme: fancy

一、前言

中秋前的需求迭代,遇到一個iframe跨域通信的問題,域名B的一個接口請求裏的字段用到裏域名A下的賬户id,原本的代碼是直接用sessionStorage.getItem(info)的方式從域名A的緩存中獲取用户信息,結果沒有拿到。

於是,我腦海裏回憶iframe相關的知識點,想到不同域名下的信息是無法直接獲取的。沒錯,這個本應該熟悉的iframe基礎知識點,我靠幾分鐘的回憶才想起來。改完需求之後,我想我是時候重拾一下iframe的相關知識了。

以下關於iframe的介紹主要來自MDN

二、基礎信息

掘金 ``` 點擊掘金按鈕前展示: ![image.png](http://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/461cbcc6603a495682d0b69a1523ade4~tplv-k3u1fbpfcp-watermark.image?) 點擊掘金按鈕後展示: ![image.png](http://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/db52c2d51e294b9c912698e16a0814c5~tplv-k3u1fbpfcp-watermark.image?) 在iframe窗口中打開頁面 使用window.open()打開窗口,該方法的第二個參數可以指定窗口名稱,我們通過window.name可以拿到iframe的name,賦值到window.open()中。 父頁面: ```js ``` **iframe頁面** ```js iframe-child
``` 可以在當前iframe窗口中打開掘金的頁面: ![image.png](http://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0a30a78c511445e3b2d9f2ade9333dd1~tplv-k3u1fbpfcp-watermark.image?) ### 3.5 referrerpolicy 表示在獲取 iframe 資源時如何發送 referrer 首部: > no-referrer: 不發送 Referer 首部。 > > no-referrer-when-downgrade (default): 向不受 TLS (HTTPS) 保護的 origin 發送請求時,不發送 Referer 首部。 > > origin: referrer 首部中僅包含來源頁面的源。換言之,僅包含來源頁面的 scheme, host, 以及 port (en-US)。 > > origin-when-cross-origin: 發起跨域請求時,僅在 referrer 中包含來源頁面的源。發起同源請求時,仍然會在 referrer 中包含來源頁面在服務器上的路徑信息。 > > same-origin: 對於 same origin (同源)請求,發送 referrer 首部,否則不發送。 > > strict-origin: 僅當被請求頁面和來源頁面具有相同的協議安全等級時才發送 referrer 首部(比如從採用 HTTPS 協議的頁面請求另一個採用 HTTPS 協議的頁面)。如果被請求頁面的協議安全等級較低,則不會發送 referrer 首部(比如從採用 HTTPS 協議的頁面請求採用 HTTP 協議的頁面)。 > > strict-origin-when-cross-origin: 當發起同源請求時,在 referrer 首部中包含完整的 URL。當被請求頁面與來源頁面不同源但是有相同協議安全等級時(比如 HTTPS→HTTPS),在 referrer 首部中僅包含來源頁面的源。當被請求頁面的協議安全等級較低時(比如 HTTPS→HTTP),不發送 referrer 首部。 > > unsafe-url: 始終在 referrer 首部中包含源以及路徑 (但不包括 fragment,密碼,或用户名)。這個值是不安全的, 因為這樣做會暴露受 TLS 保護的資源的源和路徑信息。 **不添加referrerpolicy** ```js ``` iframe窗口中會展示src設置的掘金頁面: ![image.png](http://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d2110b27bb1d48a7bed5a2806cdd4735~tplv-k3u1fbpfcp-watermark.image?) ### 3.8 srcdoc HTML5 only 該屬性是一段HTML代碼,這些代碼會被渲染到 iframe 中。如果瀏覽器不支持 srcdoc 屬性,則會渲染 src 屬性表示的內容。 ### 3.9 width 以CSS像素格式HTML5,或以像素格式HTML 4.01,或以百分比格式指定的 frame 的寬度。默認值是300。 ## 四、 腳本 內聯的框架,就像 `` 元素一樣,會被包含在 window.frames 偽數組(類數組的對象)中。 有了 DOM HTMLIFrameElement 對象,腳本可以通過contentWindow訪問內聯框架的window對象。 contentDocument屬性則引用了 `
``` **child.html** ```js iframe-child
改變背景顏色
``` **UI** ![image.png](http://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3db6d2b4da1f486585e1b2ca319ee557~tplv-k3u1fbpfcp-watermark.image?) 注:通過contentWindow改變內聯框架的屬性時,如果是本地頁面直接在Chrome中打開,點擊觸發更改事件的時候會報錯: ```js DOMException: Blocked a frame with origin "null" from accessing a cross-origin frame. ``` 通過百度查詢資料瞭解到這是由於Chrome同源策略引起的,只有在頁面加載腳本時才會出現該問題,所以我使用node啟動一個簡單的Web服務器,通過本地服務再訪問頁面功能就可以了。貼出我的node.js代碼: ```js // 目標: // 瀏覽器中輸入 http://127.0.0.1:3000/iframe/ 時,顯示 iframe.html //1. 加載 express 模塊 const express = require('express'); //2. 創建服務器 const app = express(); //3. 啟動服務器 app.listen(3000, () => { console.log('express-server is running...'); }); const path = require('path'); // 4. 監聽路由 app.get('/iframe', (req, res) => { res.sendFile(path.join(__dirname, 'view', 'iframe.html'), err => {}); }); app.get('/child', (req, res) => { res.sendFile(path.join(__dirname, 'view', 'child.html'), err => {}); }); ``` 瀏覽器中輸入 [http://127.0.0.1:3000/iframe/](url),即可訪問本地頁面: ![image.png](http://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/98ded5d8c7b047ba976c1784b31cae41~tplv-k3u1fbpfcp-watermark.image?) ### 4.2 框架內部腳本可以通過window.parent訪問父窗口對象 框架內部腳本可以通過window.parent訪問父窗口對象,進而改變父窗口的樣式 **iframe.html** ```js iframe
Iframe
父頁面box
``` **child.html** ```js iframe-child
``` **UI** ![image.png](http://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1d28f3764dda4cfa8493f85faee54be2~tplv-k3u1fbpfcp-watermark.image?) ## 五、定位和縮放 作為一個可替換元素, 可以使用 object-position 和 object-fit 來定位、對齊、縮放 ``` **child.html** ```js iframe-child
``` UI ![image.png](http://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2feb56ff5e9d4a22999eb197aaa3145e~tplv-k3u1fbpfcp-watermark.image?) **2.postMessage** 先簡單瞭解一下postMessage的功能,來源[MDN](http://developer.mozilla.org/zh-CN/docs/Web/API/Window/postMessage)。 window.postMessage() 方法可以安全地實現跨源通信。 語法 ```js otherWindow.postMessage(message, targetOrigin, [transfer]); ``` > otherWindow > > 其他窗口的一個引用,比如iframe的contentWindow屬性、執行window.open返回的窗口對象、或者是命名過或數值索引的window.frames。 > > message > > 將要發送到其他 window的數據。它將會被結構化克隆算法序列化。這意味着你可以不受什麼限制的將數據對象安全的傳送給目標窗口而無需自己序列化。[1] > > targetOrigin > > 通過窗口的origin屬性來指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示無限制)或者一個URI。在發送消息的時候,如果目標窗口的協議、主機地址或端口這三者的任意一項不匹配targetOrigin提供的值,那麼消息就不會被髮送;只有三者完全匹配,消息才會被髮送。這個機制用來控制消息可以發送到哪些窗口;例如,當用postMessage傳送密碼時,這個參數就顯得尤為重要,必須保證它的值與這條包含密碼的信息的預期接受者的origin屬性完全一致,來防止密碼被惡意的第三方截獲。如果你明確的知道消息應該發送到哪個窗口,那麼請始終提供一個有確切值的targetOrigin,而不是*。不提供確切的目標將導致數據泄露到任何對數據感興趣的惡意站點。 > > transfer 可選 > > 是一串和message 同時傳遞的 Transferable 對象. 這些對象的所有權將被轉移給消息的接收方,而發送一方將不再保有所有權。 **iframe.html** 父頁面通過postMessage向子頁面通信,將targetOrigin設置為'*',不做同源限制。 ```js iframe
Iframe
``` **child.html** 子頁面使用window.addEventListener監聽父頁面message事件,回調事件可以從event.data中獲取到父頁面傳遞的data。 ```js iframe-child
子頁面展示:
``` **UI** ![image.png](http://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/82bd56d28d024a22a9b54cecaa0e8cab~tplv-k3u1fbpfcp-watermark.image?) #### 6.2.2 子頁面向父頁面傳遞數據 子頁面通過也可以通過postMessage向父頁面傳遞數據,不同的是otherWindow為父頁面窗口,所以子頁面需要使用window.parent表示父頁面窗口(見4.2)。 **child.html** 子頁面通過postMessage向父頁面通信,將targetOrigin設置為'*',不做同源限制。 ```js iframe-child
``` **iframe.html** 父頁面使用window.addEventListener監聽子頁面message事件,回調事件可以從event.data中獲取到子頁面傳遞的data。 ```js iframe
Iframe
父頁面展示:

``` **UI** ![image.png](http://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2864099d0b35498c89c4ae97d50bbaa8~tplv-k3u1fbpfcp-watermark.image?) ## 七、總結 温故而知新,可以為師矣。古人誠不欺我。 通過再次學習iframe,對iframe瞭解加深,且對於之前時常記憶不深的跨域解決方案有了更深刻的記憶。 時不我待,感謝不斷堅持的自己,加油!