「趣学前端」关于iframe跨域通信
一、前言
之前的需求迭代,遇到一个iframe跨域通信的问题,域名B的一个接口请求里的字段用到里域名A下的账户id,原本的代码是直接用sessionStorage.getItem(info)的方式从域名A的缓存中获取用户信息,结果没有拿到。
于是,我脑海里回忆iframe相关的知识点,想到不同域名下的信息是无法直接获取的。没错,这个本应该熟悉的iframe基础知识点,我靠几分钟的回忆才想起来。改完需求之后,我想我是时候重拾一下iframe的相关知识了。
以下关于iframe的介绍主要来自MDN
二、基础信息
<iframe>,它是HTML内联框架元素,表示嵌套的browsing context。它能够将另一个HTML页面嵌入到当前页面中。
每个嵌入的浏览上下文(embedded browsing context)都有自己的会话历史记录(session history)和DOM树。包含嵌入内容的浏览上下文称为父级浏览上下文。顶级浏览上下文(没有父级)通常是由 Window 对象表示的浏览器窗口。
三、属性
3.1 allow
用户为<iframe>指定 特征策略。
3.1.1 特征策略
简单介绍一下特征策略,特征策略允许web开发者在浏览器中选择启用、禁用和修改确切特征和 API 的行为.比如内容安全策略,但是它控制的是浏览器的特征非安全行为。allow是iframe特有的特征策略,可以控制iframe使用哪些特征。
特征策略的语法
<allowlist>
*: 本特性默认在最上层和包含的内容中(iframes)允许。
'self': 本特性默认在最上层允许,而包含的内容中(iframes)使用源地址相同设定。也就是说本特性在iframe中不允许跨域访问。
'none': 本特性默认在最上层和包含的内容中(iframes)都禁止。
'src': (只在iframe中允许) 只要在src 中的URL和加载iframe用的URL相同,则本特性在iframe中允许。
其中*或'none'只允许单独使用,而'self'和'src'可以与多个源地址一起使用。
3.1.2 语法
禁止全屏模式
可以设置fullscreen的特征策略为'none'。
<iframe allow="fullscreen 'none'">
支持多个特征策略
禁止全屏模式和禁止调起支付接口,多个用分号隔开。
<iframe allow="fullscreen 'none'; payment 'none'">
3.2 csp
对嵌入的资源配置内容 安全策略。
3.2.1 内容安全策略( CSP )
内容安全策略 (CSP) 是一个额外的安全层,用于检测并削弱某些特定类型的攻|击,包括跨站脚本 (XSS (en-US)) 和数据注入攻|击等。无论是数据盗取、网站内容污染还是散发恶意软件,这些攻|击都是主要的手段。
设置内容安全策略
比如一个引入的css文件,如果我们设置了如下策略,该策略禁止任何资源的加载,除了来自cdn.example.com的样式表。
head中设置安全策略并引入外部的css文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'cdn.example.com'; " />
<title>iframe-child</title>
<link rel="stylesheet" href="./style.css" />
</head>
<body>
...
</body>
</html>
这表示样式表仅允许加载自cdn.example.com,然而该页面企图从自己的源 (http://example.com)加载。
3.3 height
以CSS像素格式HTML5,或像素格式HTML 4.01,或百分比格式指定frame的高度。默认值为150。
3.4 importance
表示 <iframe> 的 src 属性指定的资源的加载优先级。允许的值有:
auto (default):不指定优先级。浏览器根据自身情况决定资源的加载顺序
high:资源的加载优先级较高
low:资源的加载优先级较低
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
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属性则引用了
UI
注:通过contentWindow改变内联框架的属性时,如果是本地页面直接在Chrome中打开,点击触发更改事件的时候会报错:
通过百度查询资料了解到这是由于Chrome同源策略引起的,只有在页面加载脚本时才会出现该问题,所以我使用node启动一个简单的Web服务器,通过本地服务再访问页面功能就可以了。贴出我的node.js代码:
浏览器中输入 http://127.0.0.1:3000/iframe/,即可访问本地页面:
4.2 框架内部脚本可以通过window.parent访问父窗口对象
框架内部脚本可以通过window.parent访问父窗口对象,进而改变父窗口的样式
iframe.html
UI
五、定位和缩放
注:通过src传参的方式有一定的局限性,能传的数据大小有限,且复杂数据处理麻烦,所以只适合像示例中这种简单的数据的传递。
2.postMessage
先简单了解一下postMessage的功能,来源MDN。
window.postMessage() 方法可以安全地实现跨源通信。
语法
otherWindow
其他窗口的一个引用,比如iframe的contentWindow属性、执行window.open返回的窗口对象、或者是命名过或数值索引的window.frames。
message
将要发送到其他 window的数据。它将会被结构化克隆算法序列化。这意味着你可以不受什么限制的将数据对象安全的传送给目标窗口而无需自己序列化。[1]
targetOrigin
通过窗口的origin属性来指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示无限制)或者一个URI。在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。这个机制用来控制消息可以发送到哪些窗口;例如,当用postMessage传送密码时,这个参数就显得尤为重要,必须保证它的值与这条包含密码的信息的预期接受者的origin属性完全一致,来防止密码被恶意的第三方截获。如果你明确的知道消息应该发送到哪个窗口,那么请始终提供一个有确切值的targetOrigin,而不是*。不提供确切的目标将导致数据泄露到任何对数据感兴趣的恶意站点。
transfer 可选
是一串和message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。
iframe.html
父页面通过postMessage向子页面通信,将targetOrigin设置为'*',不做同源限制。
child.html
子页面使用window.addEventListener监听父页面message事件,回调事件可以从event.data中获取到父页面传递的data。
UI
6.2.2 子页面向父页面传递数据
子页面通过也可以通过postMessage向父页面传递数据,不同的是otherWindow为父页面窗口,所以子页面需要使用window.parent表示父页面窗口(见4.2)。
child.html
子页面通过postMessage向父页面通信,将targetOrigin设置为'*',不做同源限制。
iframe.html
父页面使用window.addEventListener监听子页面message事件,回调事件可以从event.data中获取到子页面传递的data。
UI
七、总结
温故而知新,可以为师矣。古人诚不欺我。
通过再次学习iframe,对iframe了解加深,且对于之前时常记忆不深的跨域解决方案有了更深刻的记忆。
时不我待,感谢不断坚持的自己,加油!