关于Iframe 你不知道的那些事?

194 阅读4分钟

如何操作页面内嵌的iframe,和iframe传递消息?

1、 通过 postMessage 的方式进行监听双方的message,当然这种前提是需要你有 iframe嵌入页面的源代码可以进行监听,下面看代码 使用iframe 页面


<template>
    <div>
        <iframe ref="iframedom" src="http://localhost:8080/" width="600px" height="400px" frameborder="0"></iframe>

        <div>
            <div v-for="item in temArr" :key="item">
                <p>{{ item }}</p>
                <button  @click="interTo(item)">插入</button>
            </div>
        </div>
    </div>
</template>

<script setup lang="ts">
//  在使用webpack 构建的项目 中使用  postMessage 会出现 自动触发 一次 message 的监听事件
//  解决方案: 在vite 构建的不会出现,但我们保险起见也还是一样的操作
/**
 *      let _window = iframedom.value?.contentWindow
        let _document = iframedom.value?.contentDocument
        _window.postMessage(item,'http://localhost:8080/')  我们可以给 postMessage 的第一个参数 设置为一个对象然后,对象里面设置type标识,这样就可以避免自动触发 message 事件
        _window.postMessage({type:'inserInto',data:item},'http://localhost:8080/')

 */
//  在使用 postMessage 的时候,我们最好使用 https 的方式,这样就可以避免被劫持
//  postMessage 通信的时候,我们最好使用 域名的方式,这样就可以避免被劫持
//  postMessage  有一定的局限性 ,例如我们需要使用 三方库的情况 ,我们无法使用 postMessage 来进行通信
// iframe 里面任何一个dom=> 获取到操作元素的dom => 修改元素的样式 => 修改元素的属性 => 修改元素的文本内容


import {ref, onMounted} from 'vue'
const iframedom = ref(null)
const temArr = ['hello', '大家好', 'buzhidao', '我是谁', '我在哪', '我为什么在这里']
const interTo = (item:string)=>{
     if(iframedom.value){
         let _window = iframedom.value?.contentWindow
         let _document = iframedom.value?.contentDocument
       _window.postMessage({type:'inserInto',data:item},'http://localhost:8080/')
     }
}
onMounted(()=>{
    // 这是接收 iframe 的传输的数据
       window.addEventListener('message', (e) => {
             if(e.data.type==='save'){
                 console.log(e.data.data)
            }
       })

})
</script>

iframe页面

<template>
  <div id="app">
    <textarea class="textarea" name="" v-model="texts" id="" cols="30" rows="10" @blur="blurText"></textarea>
    <button @click="save">保存</button>
  </div>
</template>

<script setup lang="ts">
import { onMounted, ref } from "vue";

const texts = ref('')
// 光标位置
const cursorIndex = ref(0)

const save = () =>{
  // window.top // 获取父级窗口
  //  将数据传递给父级
  window.parent.postMessage({type:'save',data:texts.value},'*')
}
const blurText = (el) =>{
  cursorIndex.value = el.target.selectionStart
}
onMounted(()=>{
   window.addEventListener('message', (e) => {
    if(e.data.type==='inserInto'){
      // texts.value = e.data.data
      texts.value = texts.value.slice(0, cursorIndex.value) + e.data.data + texts.value.slice(cursorIndex.value)
    }
  })
})
</script>

2、 通过直接操作DOM 元素,我们不能修改代码,或者调用的iframe 是 第三方的时候使用

<template>
    <div>
        <iframe ref="iframedom" src="http://localhost:8080/" width="600px" height="400px" frameborder="0"></iframe>

        <div>
            <div v-for="item in temArr" :key="item">
                <p>{{ item }}</p>
                <button  @click="interTo(item)">插入</button>
            </div>
        </div>
    </div>
</template>

<script setup lang="ts">
import {ref, onMounted} from 'vue'
const iframedom = ref(null)
const temArr = ['hello', '大家好', 'buzhidao', '我是谁', '我在哪', '我为什么在这里']
const interTo = (item:string)=>{
    if(iframedom.value){
        let _document =  iframedom.value && iframedom.value.contentDocument
        if(_document){
            let textAreaDom = _document.querySelector('.textarea')
            let _oldVal = textAreaDom.value
            let _postion = textAreaDom.getAttribute('data-position')
            let _newVal = _oldVal.slice(0,Number(_postion)) + item + _oldVal.slice(Number(_postion))
            textAreaDom.value = _newVal
        }
    }
}
onMounted(()=>{

    // dom=> 获取 操作
    if(iframedom.value){
        iframedom.value.addEventListener('load',()=>{
            console.log(iframedom.value, 'iframedom.value')
            let _document =  iframedom.value?.contentDocument;
            console.log(_document, '-_document')
            if(_document){
                let textAreaDom = _document.querySelector('.textarea')
                textAreaDom.addEventListener('blur',(e)=>{
                    console.log(textAreaDom.value)
                    textAreaDom.setAttribute('data-position', e.target.selectionStart + '')
                })
            }
        })

    }
})
</script>

<style scoped>

</style>

3、 无法嵌入百度页面 — X-Frame-Options响应限制

百度使用了 X-Frame-Options 响应头来限制其在 iframe 中的嵌套。X-Frame-Options 是一种安全策略,可以由网站的服务器设置,用于控制页面是否允许在 iframe 中嵌套。当网站设置了 X-Frame-Options 为 DENY 或 SAMEORIGIN,浏览器将不允许将该页面在 iframe 中加载,以防止点击劫持等安全问题。

DENY: 禁止所有页面在 iframe 中嵌套,无论来源域名是什么。

SAMEORIGIN: 允许同源页面在 iframe 中嵌套,但禁止不同源的页面进行嵌套。

allow-from XXX.com: 允许被指定域名的网站嵌套。

4、http无法嵌入https

解决方案:

(1)使用 HTTPS 协议: 主页面也迁移到 HTTPS 协议,这样就不会涉及到 HTTP 和 HTTPS 跨域的问题。

(2)服务器代理: 在服务器端设置代理,让服务器请求 HTTPS 页面的内容,然后将结果传递给 HTTP 页面的前端,由前端进行展示。

(3)使用 Subresource Integrity (SRI) : 如果目标 HTTPS 页面提供了 SRI 支持,可以使用 SRI 来加载和验证脚本和样式。

最佳做法是将页面迁移到 使用 HTTPS 协议

5、cookie设置共享问题

(1)浏览器限制了通过 iframe 中的页面使用 set-cookie 标头来设置 Cookie。这是出于安全考虑,防止跨域 Cookie 污染攻击。当在 iframe 中加载一个来自不同域的页面时,该页面无法通过设置 set-cookie 标头来在主页面的域中设置 Cookie。

(2)这种限制是由同源策略 (Same-Origin Policy) 引起的,它要求网页只能访问来自相同域的资源。Cookie 是一种用于跟踪会话状态和存储用户数据的机制,在跨域的情况下,Cookie 可能被恶意网站滥用,因此浏览器禁止了在跨域 。

解决方案: 1、使用同域代理: 在服务器端设置代理,让服务器请求目标域的资源,然后将结果传递给前端,由前端处理 Cookie。这样可以避免跨域 Cookie 问题。

2、使用 token: 通过在请求中使用 token 来进行身份验证和会话管理,而不依赖于 Cookie。比如我直接嵌入掘金的页面,使用情况是属于正常的

3、CORS: 可以考虑设置目标网站服务器 CORS 响应头,以允许特定域名的请求访问资源。

这是目前在使用中和查看的一些问题,可能不全面,毕竟还没在真正项目中使用过,在项目中就得具体问题具体分析了,各位网友多多指教!