iframe相关及跨域解决方案 |
您所在的位置:网站首页 › 端口丢失怎么解决 › iframe相关及跨域解决方案 |
[toc] 获取iframe的window、document页面: 通过contentDocument或者contentWindow: const frame = document.getElementById('frame') const fwindow = frame.contentWindow const fdoc1 = frame.contentDocument const fdoc2 = frame.contentWindow.document如果要读取或者操作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,在父页面中调用这个方法发现跨域了: 这种情况下,父页面和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); }调用结果成功: 如果两个网页不同源,就无法拿到对方的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)可以通过postMessage、片段标识符、window.name来解决,下面主要介绍前两个: window.postMessagewindow.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() } }) }执行结果: 如果子页面执行完方法需要向父页面作出通知,可以在子页面中通过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); }) }执行结果: 关于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)() }) }结果: 同样的,子窗口也可以改变父窗口的片段标识符。 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 |