【知识点】关于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了解加深,且对于之前时常记忆不深的跨域解决方案有了更深刻的记忆。 时不我待,感谢不断坚持的自己,加油!