iframe内嵌及跨域通信

本文探讨了iframe在网页开发中的应用,详细讲解了如何利用iframe进行跨域数据通信,包括document.domain和postMessage方法。同时,深入分析了iframe带来的安全问题及防范措施。
摘要由CSDN通过智能技术生成

前言

对于iframe标签,现在都应该用的很少了因为它存在一些问题,比如安全问题或者能耗高,但最近笔者就使用了它做网页内嵌并跨域处理了数据,所以记录记录。

iframe基本概念

<iframe src="demo.html" height="300" width="500" name="demo" scrolling="auto" sandbox="allow-same-origin"></iframe>

iframe的一些基本属性:

  • src iframe页面地址,有同域跨域之分
  • height iframe高度
  • width iframe宽度
  • name iframe命名,可通过window.frames[xxx]被调用
  • scrolling iframe滚动模式
  • sandbox html5新特性,用于限制iframe的功能

使用iframe的正确姿势

可以通过以下 选择器来获取iframe节点(window.frames['xxx']的方式好像已经不能用了):
document.getElementById('iframeId')
document.getElementsByName('iframeName')
document.getElementsByClassName('iframeClassName')
document.getElementsByTagName('iframe')
document.querySelector('#iframeId')
document.querySelector('.iframeClassName')

我们可以通过contentWindowcontentDocument两个API获取iframe的window对象和document对象。

let iwindow = iframe.contentWindow; // 获取iframe的window对象
let idoc = iframe.contentDocument; // 获取iframe的document对象

iframe使用父级内容的正确姿势

我们通过window.selfwindow.parentwindow.top这三个属性分别获取自身window对象,父级window对象,顶级window对象。

看图说话

在这里插入图片描述
所以:

iframe1.self === iframe1
iframe1.parent === iframe2
iframe2.parent === window
iframe1.top === window

同域/跨域

什么是同域什么跨域咧?同域跨域的区别在哪咧?我们一般会使用iframe来进行父子页面的通信,但父子页面是否同域决定了它们之间能否进行通信

js遵循同源策略,即协议域名端口一致,否则都算跨域。

同源策略 是由Netscape提出的一个著名的安全策略,现在所有支持JavaScript 的浏览器都会使用这个策略。实际上,这种策略只是一个规范,并不是强制要求,各大厂商的浏览器只是针对同源策略的一种实现。它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。

跨域 简单的来说,指的是两个资源非同源。出于安全方面的考虑,页面中的JavaScript在请求非同源的资源时就会出 跨域问题 ——即跨域请求,这时,由于同源策略,我们的请求会被浏览器禁止。也就出现了 我们常说的 跨域 问题。

通过这个图可以进一步帮助我们理解同域和跨域。

在这里插入图片描述

iframe跨域通讯之document.domain

对于主域相同子域不同的两个页面,我们可以通过document.domain + iframe来解决跨域通信问题。

举个🌰,网页a(http://www.easonwong.com)和网页b(http://script.easonwong.com),两者都设置document.domain = 'easonwong.com'(这样浏览器就会认为它们处于同一个域下),然后网页a再创建iframe上网页b,就可以进行通信啦!

网页a

document.domain = 'easonwong.com';
var ifr = document.createElement('iframe');
ifr.src = 'http://script.easonwong.com';
ifr.style.display = 'none';
document.body.appendChild(ifr);
ifr.onload = function(){
    let doc = ifr.contentDocument || ifr.contentWindow.document;
    // 在这里操纵b.html
};

网页b

document.domain = 'easonwong.com';

iframe跨域通讯之postMessage

postMessage是html5的新特性,具体介绍看传送门。

postMessage介绍

MDN postMessage

兼容性 IE8以上

can I use

我们可以通过html5这个新特性进行iframe间的跨域通信,使用postMessage进行数据传递,通过Message监听通信事件。举个🌰

网页a

document.domain = 'easonwong.com';
var ifr = document.createElement('iframe');
ifr.src = 'http://script.easonwong.com';
ifr.style.display = 'none';
document.body.appendChild(ifr);
// 发送数据
ifr.postmessage('hello, I`m a', 'http://script.easonwong.com');

网页b

// 监听message事件
window.addEventListener('message', receiver, false);
function receiver(e) {
    if (e.origin == 'http://www.easonwong.com') {
        if (e.data == 'hello, I`m a') {
            e.source.postMessage('hello, I`m b', e.origin);信息
        }
    }
}

iframe的安全问题

iframe小广告

很让我们讨厌iframe的一点,就是很多网站都会有各种让人防不胜防的小广告,它们大多就是用通过iframe实现的,本来想点击某个播放按钮,结果直接跳到不知道去了哪个新世界去了。

所以我们一定要注意在用iframe的同时,要防止我们被iframe了。

防嵌套页面操作

在前端领域,我们可以通过window.top来防止我们页面被嵌套。

if(window != window.top){
    window.top.location.href = myURL;
}

或者通过window.location.host来检测是否跨域了

if (top.location.host != window.location.host) {
  top.location.href = window.location.href;
}

而后端也可以做对应的防范措施,通过设置X-Frame-Options响应头来确保自己网站的内容没有被嵌到别人的网站中去,也从而避免了点击劫持 (clickjacking) 的攻击。

CSP

内容安全策略(CSP)用于检测和减轻用于 Web 站点的特定类型的攻击,例如 XSS 和数据注入等。

MDN CSP

通过CSP配置sandbox和child-src可以设置iframe的有效地址,它限制适iframe的行为,包括阻止弹出窗口,防止插件和脚本的执行,而且可以执行一个同源策略。

用法

  • 我们可以在html头部中加上<meta>标签
<meta http-equiv="Content-Security-Policy" content="child-src 'unsafe-inline' 'unsafe-eval' www.easonwong.com">
  • 或者通过HTTP头部信息加上Content-Security-Policy字段


  • 现在回头来看看业务上的使用:

    逻辑上简单来说就是,用一个iframe标签从a网页跳到另一个b网页,并且在b页面上操作后将数据拿回a网页,效果代码如下:
    HTML:

    <el-dialog title="选址" 
          width = "70%" 
          height = '500px'
          :visible.sync="addrSelectDialogVisible" 
          :before-close="handleCloseAddrSelectDialog" 
          :modal-append-to-body="false">
          <div class="addr-select-box">
            <!-- start of 详情-->
            <iframe 
            src="http://134.96.249.135:8088/ass/webLogin.do?areaCode=571" frameborder="0" width="100%" height="100%" ref="iframeId">
            </iframe>
            <!-- end of 详情-->
            </div>
        </el-dialog>
    

    JS:

    //注册监听选址的消息
          registerSelectAddrMessage( ){
            let self = this ;
            if (navigator.appName=="Microsoft Internet Explorer"&&(navigator.appVersion.match(/8./i)=="8."||navigator.userAgent.indexOf("MSIE 8.0")>0||navigator.appVersion.match(/7./i)=="7.")){
                window.attachEvent('onmessage',function(e){ self.handleSelectAddrMessage( e.data );});
            }
            else {
                window.addEventListener('message',function(e){ self.handleSelectAddrMessage( e.data );},false);
            }
          },
          //监听选址信息
          handleSelectAddrMessage( addrMessageData ){
            let self = this ;
            //转换为标准的JSON字符串
            let addrMsgObj = {};
            if( typeof addrMessageData === 'string' && addrMessageData.indexOf( 'c3Code' ) > 0 ){
              var reg = /({\S*?:)|(,\S*?:)/g
              var formatedJSONStr = addrMessageData.replace( reg , function( a , m1 , m2 ){
                  if( m1 ){   //如果第一个分组匹配上
                    return '{"' + a.slice( 1 , -1 ) + '":';
                  }
                  else{
                    return ',"' + a.slice( 1 , -1 ) + '":';
                  }   
              } );
              formatedJSONStr = formatedJSONStr.replace( /\'/g , '"' );
              let addrMsgObj = {};
              addrMsgObj = JSON.parse( formatedJSONStr );
              if( addrMsgObj.c3Code ){  
                self.formReviewList[self.listIndex].resourceC3 = self.cityCodeMap[ addrMsgObj.c3Code ]  ;
                self.formReviewList[self.listIndex].resourceC4 = addrMsgObj.c4name  ;
                self.formReviewList[self.listIndex].resourceAddress = addrMsgObj.address ;
                self.formReviewList[self.listIndex].resourceAddressId = addrMsgObj.addressId ;
                self.formReviewList[self.listIndex].resourcePort = addrMsgObj.epon + ',' + addrMsgObj.gpon + ',' + addrMsgObj.exchName ;
                //关闭选址的对话框
                self.addrSelectDialogVisible = false ;
                self.listIndex = '' ;
              }
            }
          },
    
    created(){
      let self = this;
      self.getWorkOrderList();
      //监听选址的事件响应
      self.registerSelectAddrMessage();
    },
    

    为了每次进入时都是新的b页面,做个重载,第一种方式没行通,用了第二种。
    重载:

    if( self.$refs.iframeId ){
      // 第一种重载必须同域
      // self.$refs.iframeId.contentWindow.location.reload( true );
      // 第二种每次赋值路径,可同域可跨域
      self.$refs.iframeId.src = 'http://134.96.249.135:8088/ass/webLogin.do?areaCode=571'
    };
    

    效果:
    在这里插入图片描述
    在这里插入图片描述
    所以在代码里做了监听** ★,°:.☆( ̄▽ ̄)/$:.°★ **。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值