iframe相关及跨域解决方案

您所在的位置:网站首页 端口丢失怎么解决 iframe相关及跨域解决方案

iframe相关及跨域解决方案

2024-03-06 17:06| 来源: 网络整理| 查看: 265

[toc]

获取iframe的window、document

页面:

通过contentDocument或者contentWindow: const frame = document.getElementById('frame') const fwindow = frame.contentWindow const fdoc1 = frame.contentDocument const fdoc2 = frame.contentWindow.document

20210724172850.png

通过window.frames和iframe的name属性.这种方式直接获取到的是iframe页面的window对象: const frame = window.frames['frameSon'] console.log(`frame:`,frame); console.log(`frame.document:`,frame.document);

20210724172625.png

读取或者调用iframe内部的方法

如果要读取或者操作iframe中的内容,要确保在iframe加载完成后再进行操作,如果iframe还未加载完成就开始调用里面的方法或变量,会产生错误。判断iframe是否加载完成有两种方法:

通过上面第一种方式获取到得iframe是iframeDOM元素,可以直接通过frame.onload回调来判断iframe加载完成

通过上面第一种方式获取到得iframe是iframe的window对象,但并没有onload方法,所以可以用当前页面的window.onload回调或者 document.readyState=="complete" 来判断

例如,我们要调用子iframe中的test方法,两种方式:

第一种方式:

const frame = document.getElementById('frame') const fdoc = frame.contentDocument || frame.contentWindow.document frame.onload = () => { console.log(`frame.contentWindow.test:`, frame.contentWindow.test); frame.contentWindow.test() }

第二种方式:

const frame = window.frames['frameSon'] const fdoc = frame.document window.onload = () => { console.log(`frame.test:`, frame.test); frame.test() } 主域相同、二级域名不同的两个页面获取对方dom导致的跨域问题

主页面是a.baidu.com,以iframe的形式引入了b.baidu.com子页面。子页面中声明了一个全局方法test,在父页面中调用这个方法发现跨域了:

20210724175304.png

这种情况下,父页面和iframe主域相同,只是二级域名不同,可以通过两个页面同时同时把document.domain设置为相同的主域来解决:

另外,通过给主域相同、二级域名不同的页面设置相同的document.domain,也可以解决cookie的跨域问题。Cookie 是服务器写入浏览器的一小段信息,只有同源的网页才能共享。但浏览器允许通过设置document.domain共享 Cookie。

父页面:

Document page1 document.domain = 'baidu.com' const frame = window.frames['frameSon'] const fdoc = frame.document window.onload = () => { console.log(`frame.test:`, frame.test); frame.test() }

iframe子页面:

Document page2d document.domain = 'baidu.com' function test(){ console.log(`222:`,222); }

调用结果成功:

20210724175939.png

两个页面域名完全不同

如果两个网页不同源,就无法拿到对方的DOM。例如iframe引入和window.open方法打开的窗口,它们之间的窗口无法通信。

例如,父页面是baidu.com,子页面是zijie.com,父页面向调用子页面中的全局方法test。

子页面中声明了test全局方法:

function test(){ console.log(`222:`,222); }

父页面通过iframe引入子页面并尝试调用其test方法:

page1 const frame = window.frames['frameSon'] const fdoc = frame.document window.onload = () => { console.log(`frame.test:`, frame.test); frame.test() }

结果报错跨域:

(index):54 Uncaught DOMException: Blocked a frame with origin "http://baidu.com:3333" from accessing a cross-origin frame. at window.onload (http://baidu.com:3333/:54:42)

20210724181519.png

可以通过postMessage、片段标识符、window.name来解决,下面主要介绍前两个:

window.postMessage

window.postMessage方法允许跨窗口通信,不论这两个窗口是否同源。

大概的实现逻辑是:

一个窗口可以获得对另一个窗口的引用(比如iframe的contentWindow属性、执行window.open返回的窗口对象、或者是命名过或数值索引的window.frames),调用这个窗口引用上的postMessage方法分发一个MessageEvent 消息。然后子窗口中通过监听message事件来获取父窗口传递的消息即可。

子页面向父窗口发送消息同理,不同一点是子窗口可以通过MessageEvent.source来获取父窗口的引用。

语法:

otherWindow.postMessage(message, targetOrigin);

otherWindow :要给其发送消息的目标窗口的引用;

第一个参数message : 具体的消息内容;

第二个参数targetOrigin :指定哪些窗口能接收到消息事件。可以指定接收消息的窗口的源(origin),即"协议 + 域名 + 端口"。也可以设为"*",表示不限制域名,向所有窗口发送。 在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。这个机制用来控制消息可以发送到哪些窗口。

监听message时间的时候,可以通过MessageEvent对象获取下面三个参数:

MessageEvent.source:发送消息的窗口引用 MessageEvent.origin: 发送消息的来源origin MessageEvent.data: 消息内容

看一下如何通过postMessage解决上面出现的跨域问题:

父页面可以获取到子页面的引用后,调用其postMessage方法并将需要调用的方法通过参数传递过去:

window.onload = () => { frame.postMessage({ fn: 'test' }, "http://zijie.com:4444") }

子页面中监听message时间,并对event.origin和event.data作出判断后,调用相应的方法:

function test(){ console.log(`222:`,222); } window.onload = function(){ window.addEventListener('message', e => { console.log(`e:`,e); if(e.origin === 'http://baidu.com:3333' && e.data.fn === 'test'){ test() } }) }

执行结果:

20210725225754.png

如果子页面执行完方法需要向父页面作出通知,可以在子页面中通过event.source获取父页面的引用,调用其poseMessage方法,然后父页面中通过监听message时间实现。

子页面:

function test(){ console.log(`222:`,222); } window.onload = function(){ window.addEventListener('message', e => { console.log(`e:`,e); if(e.origin === 'http://baidu.com:3333' && e.data.fn === 'test'){ test() // 向父页面发出通知 e.source.postMessage('执行完毕', '*') } }) }

父页面:

window.onload = () => { frame.postMessage({ fn: 'test' }, "http://zijie.com:4444") // 接受子页面通知 window.addEventListener('message', e => { console.log(`e.data:`,e.data); }) }

执行结果:

20210725230139.png

关于postMessage的安全问题

如果不希望从其他网站接收message,不要为message事件添加任何事件侦听器。 这是一个完全万无一失的方式来避免安全问题。

如果确实希望从其他网站接收message,始终使用MessageEvent.origin和 MessageEvent.source 属性验证发件人的身份。 任何窗口(包括例如evil.example.com)都可以向任何其他窗口发送消息,

当使用postMessage将数据发送到其他窗口时,始终指定精确的目标origin,而不是*。 恶意网站可以在我们不知情的情况下更改窗口的位置,因此它可以拦截使用postMessage发送的数据。

片段标识符fragment identifier

片段标识符(fragment identifier)指的是URL的#号后面的部分。比如 “example.com/x.html#frag… 的“#fragment”。如果只是改变片段标识符,页面不会重新刷新。

父页面把需要调用的方法以hash的形式加在iframe地址后面:

const frame = document.getElementById('frame') window.onload = () => { // 通过hash改变iframe的src,不会导致刷新 frame.src = `http://zijie.com:4444#test` }

子页面监听hashchage事件来获取当前地址的hash值:

function test(){ console.log(`222:`,222); } window.onload = function(){ window.addEventListener('hashchange', e => { console.log(`e:`,e); // 通过location.hash获取到父页面传递过来的信息 console.log(`window.location.hash:`,window.location.hash); const fn = window.location.hash.slice(1) eval(fn)() }) }

结果:

20210725233902.png

同样的,子窗口也可以改变父窗口的片段标识符。

parent.location.href= target + "#" + hash; REF

(9条消息) postMessage安全性问题_Exploit的小站~-CSDN博客

前端跨域常见解决方案(包括反向代理)_哔哩哔哩_bilibili

window.postMessage - Web API 接口参考 | MDN

利用JS对iframe父子(内外)页面进行操作的方法教程_javascript技巧_脚本之家

浏览器同源政策及其规避方法 - 阮一峰的网络日志



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3