iframe跨域踩坑

前言

工做中,有些系統是利用的iframe。有一次忽然遇到了一個使人費解的跨域報錯,沒有發請求,卻報了「Error:Blocked a frame with origin...from accessing a cross-origin frame」的跨域錯誤的問題,當時本身一臉懵(恕我無知)……原來是由於本身的一句window.parent.document.scrollTop,萬萬想不到,iframe之間的通訊也會存在跨域問題,趕忙去研究一下。javascript

一.iframe跨域

其實前言的本質,就是由於父、子窗口這兩個頁面不一樣源,可是卻想在iframe中獲取父窗口的dom,形成了跨域問題。html

若非同源,會受到三種跨域限制:一是ajax請求限制;二是DOM沒法得到;三是Cookie、LocalStorage 和 IndexDB 沒法讀取。java

本文主要講第二點,不知足同源策略的兩個網頁是沒法拿到對方的DOM的ajax

這裏須要特別說明的是:阮一峯老師在文章 瀏覽器同源政策及其規避方法裏說到父窗口想獲取子窗口的DOM,由於跨源致使報錯。chrome

可是經我這邊用chrome操做,並無報錯,是能夠獲取到dom的,我大膽猜測多是chrome更新了安全策略吧。跨域

舉個栗子:瀏覽器

<!-- 父窗口http://172.18.12.109:8080/#/Parent,裏面分別嵌了同源和跨域兩個子窗口-->
<template>
  <div class="">
    <div>父窗口</div>
    <iframe src="http://172.18.12.109:8080/#/Child_Same" id="iframe1"></iframe>
    <iframe src="http://172.18.12.109:8082/#/Child_Diff" id="iframe2"></iframe>
  </div>
</template>

<script>
export default {
  name: 'Parent',
  data () {
    return {
    }
  },
  mounted( ) {
    console.log('document.getElementById("iframe1").contentWindow.document',document.getElementById("iframe1").contentWindow.document);
    console.log('document.getElementById("iframe2").contentWindow.document',document.getElementById("iframe2").contentWindow.document);
  }
}
</script>
複製代碼
<!-- 同源子窗口http://172.18.12.109:8080/#/Child_Same-->

<template>
  <div class="">
    <div>同源子窗口</div>
  </div>
</template>

<script>

export default {
  name: 'Child_Same',
  data () {
    return {
      
    }
  },
  mounted() {
    console.log('同源 window.parent.document',window.parent.document);
  }
}
</script>
複製代碼
<!-- 跨域子窗口http://172.18.12.109:8081/#/Child_Diff-->
<template>
  <div class="">
    <div>跨域子窗口</div>
  </div>
</template>

<script>

export default {
  name: 'Child_Diff',
  data () {
    return {
      
    }
  },
  mounted() {
    console.log('跨域 window.parent.document',window.parent.document);
  }
}
</script>
複製代碼

image

父窗口獲取子窗口的DOM在同源和跨域下均可以獲取;而子窗口獲取父窗口的DOM在跨域時會報錯,同源能夠獲取到。安全

二.解決方法

targetWindow.postMessage(data,targetOrigin,transfer)

這是跨窗口發送消息的方法。bash

逐一解釋一下參數:dom

targetWindow: 窗口的引用。能夠是當前窗口window,也能夠是一個窗口得到的其它窗口的引用,能夠爲:

  • Window.parent
  • HTMLIFrameElement.contentWindow
  • Window.open
  • Window.opener
  • Window.frames +索引值

data: 傳遞的消息

targetOrigin:接收消息的目標窗口的源(協議 + 域名 + 端口)。能夠設爲*,表示不限域名,可向全部窗口發送。

transfer : 可選參數

好的,那麼如何接收發來的消息呢?

目標窗口對message事件進行監聽,便可得到發送過來的消息

window.addEventListener("message", receiveMessage, false);

function receiveMessage(event)
{
  // For Chrome, the origin property is in the event.originalEvent
  // object. 
  // 這裏不許確,chrome沒有這個屬性
  // var origin = event.origin || event.originalEvent.origin; 
  var origin = event.origin
  if (origin !== "http://example.org:8080")
    return;

  // ...
}
複製代碼

因此前言子獲取父窗口dom的問題能夠這樣解決:

//父窗口向iframe發消息
 document.getElementById("iframe").contentWindow.postMessage({scrollTop:document.body.scrollTop},'http://172.18.12.109:8082');
複製代碼
//iframe接收消息
 window.addEventListener('message', (e)=>{
        let origin = event.origin || event.originalEvent.origin; 
        if (origin !== 'http://172.18.12.109:8080') {
          return;  
        }else {
           console.log(e.data);
           //{scrollTop: 0}
        }
      },false);
複製代碼

再舉一個工做中遇到的栗子:

在iframe中不一樣源的子窗口頁面間實現跳轉

具體以下:

有一個系統,是由提供了整個頁面導航欄的父窗口Aiframe嵌入的不一樣源子窗口組成的。不一樣一級導航對應不一樣模塊,同一模塊下能夠經過路由進行頁面跳轉,而不一樣模塊只能經過url跳轉。

如今要實現點擊模塊B中的一個按鈕從B跳轉到模塊C的一個頁面,可是不能新開窗口跳轉,仍是要在父窗口裏,由於要外部導航欄始終不變,只是內部頁面跳轉。

所以這裏須要用到在子窗口B和父窗口A通訊,而後在A中打開C這個窗口。

與前言一樣的,此處爲父向子發送消息,子窗口接收消息。

示例代碼以下:

<!-- 父窗口http://172.18.12.109:8080/#/Parent-->
<template>
  <div class="">
    <div>外部父窗口</div>
    <!-- 跨域 -->
    <iframe src="http://172.18.12.109:8081/#/Child_Diff1" id="iframe1"></iframe>
  </div>
</template>

<script>
export default {
  name: 'Parent',
  data () {
    return {
    }
  },
  mounted( ) {
  //目標父窗口接收子窗口發送的消息
     window.addEventListener('message', (e)=>{
        let origin = event.origin || event.originalEvent.origin; 
        if (origin !== 'http://172.18.12.109:8082') {
          return;  
        }else {
        //更換iframe的src,實現iframe頁面跳轉
          document.getElementById("iframe1").src=e.data.link;
        }
      },false);
    }
  }
}
</script>
複製代碼
<!-- 內部跨域子窗口1http://172.18.12.109:8081/#/Child_Diff1-->
<template>
  <div class="">
    <div>內部跨域子窗口1</div>
    <button @click="onClick">點擊向父窗口發送消息</button>
  </div>
</template>

<script>
export default {
  name: 'Child_Diff1',
  data () {
    return {
      
    }
  },
  methods: {
    onClick () {
      let message =  {type: 'open', link:'http://172.18.12.109:8082/#/Child_Diff2'};
      //子窗口向父窗口發送消息,消息中包含咱們想跳轉的連接
      window.parent.postMessage(message,'http://172.18.12.109:8080');
    }
  }
}
</script>
複製代碼

實現效果:

image

所以,最終可實現: 點擊跨域子窗口1的按鈕,向父窗口發送消息,告訴父窗口要跳轉的跨域子窗口2的連接,父窗口接收到消息後,更改iframe src的url,便可實現不用打開新頁面,iframe子窗口的變化。

相關文章
相關標籤/搜索