window.postMessage()跨域处理

2,111 阅读5分钟

window.postMessage方法可以安全的启用window对象之间的跨源。例如,在页面和它产生的弹出窗口之间,或者在页面和嵌入其中的iframe之间。

一般, 当且仅当它们源自的页面共享相同的协议、主机、端口号(同源策略)时,允许不同页面上的脚本互相访问。但如果跨域(不符合同源策略时),是不允许页面上的脚本互相访问。

postMessage()

transInfoToTargetWindow.postMessage(needTransmitMess, targetOrigin, [transfer])


-transInfoToTargetWindow

是将要接收消息的窗口的引用。获得此引用的方法包括:

--window.open (生成一个新的窗口,然后引用它)。

--window.opener(产生(通过window.open打开的)目标窗口的引用)

--HTMLFrameElement.contentWidow(父窗口嵌入iframe的引用)

--Window.parent(从嵌入式内部引用父窗口<iframe>

--window.frames+索引值(命名或数字)(window.frames[ 0 ]与document.getElementsByTagName( "iframe" )[ 0 ].contentWindow是相同的)


-needTransmitMess

此参数是要发送到目标窗口的数据,将会自动被结构化克隆算法序列化。这意味着可以将各种各样的数据对象安全的传递到目标窗口,而无需自己序列化。


-targetOrigin

指定要发送数据的源。可以是字符串“*”,表示任意源。也可以是指定的url。如果在发送数据的时候, transInfoToTargetWindow参数的引用与targetOrgin参数提供的内容不匹配,则不会触发此事件。记得要始终提供具体的targetOrigin,而不是使用“*”。是为了防止若未提供特定目标时,会泄露发送的数据到一些恶意网站。

-transfer(可选参数)

是与传递数据一起传输的Transferable对象序列。这些对象的所有权将提供给目标端,并且它们在发送端不下可用。(大多浏览器不支持此参数,不做详述)

目标页面是如何得到传递的数据呢?

目标页面可以通过

window.addEventListener(“message是要传递的数据”,(e)=>{console.log(e)}, false)

方法获得传递过来的属性及相关父页面数据。

e对象下的属性有:

data:源窗口传递给目标窗口时的数据。

orgin:发送消息数据的窗口的url。注意此来源不能保证是该窗口的当前或未来源,因为该窗口可能已经被导航到调用postmessage后的其他位置。

source:对发送消息的window对象的引用;可以使它来建立两个不同来源窗口之间的双向通信。

注意:

安全隐患:

1.如果不希望从其他站点接收消息,请不要weimessage事件添加任何事件监听。

2.若确实要使用message监听事件,请始终使用origin和可能的source属性验证发消息的窗口的身份。(因为这样指定来消息的来源。)

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

function receiveMessage(event)
{
  if (event.origin !== "http://example.org:8080")
    return;

}


postMessage  ----iframe嵌入式时使用方式

主页面。此页面既传递数据给子页面,又获取子页面的数据

import React, {Component} from "react";
class Main extends Component {    
  constructor(props){        
    super(props);        
    this.state = {            
      showValueIframe :'main父页面'        
    }    
  }
  componentDidMount (){        
    console.log("父页面的挂载")        
    window.addEventListener('message', (e)=> {//获取子页面传递回来的数据            
      console.log(e.data,'main');            
      this.setState({                
        showValueIframe: e.data            
      })        
    },false);    
  }    
  clickIframe = ()=>{        
    let data ='我是父页面传到子页面的值';
        //给子页面传递数据        
    document.getElementById('iframe').contentWindow.postMessage(data, "http://localhost:3001/");    
  }       
  render(){        
    return(            
      <div>                
        {this.state.showValueIframe}               
        <button onClick = {this.clickIframe}>iframe跨域点击事件</button>                             
        <iframe id = "iframe" src = "http://localhost:3001/" >
        </iframe>            
      </div>        
    )    
  }}
export default Main;

被嵌入到主页面的js,此页面既传递数据给父页面,又获取父页面的数据

import React, {Component} from "react";
class Secode extends Component {    
  constructor(props){        
    super(props);        
    this.state = {            
      showValueIframe :'子页面'        
    }    
  }       
  componentDidMount(){        
    console.log("子页面的挂载")        
    //获取父页面传递回来的数据        
    window.addEventListener('message', (e)=> {            
      console.log(e.data,e,'second');
      this.setState({                
        showValueIframe: e.data            
      })        
    },false);    
 }    
clickIframe = ()=>{        
   window.parent.postMessage('子页面传到父页面的值', "http://127.0.0.1:3000/");    
}    
render(){        
  return(            
    <div>                
      {this.state.showValueIframe}               
      <button onClick = {this.clickIframe}>传输数据给父页面</button>
                      
    </div>        
  )    
}}
export default Secode;

1.初始页面


2.当点击了父页面的clickIframe方法,传递给子页面

父页面:此处获取的iframe的src上的url要等于postMessage上的目标源。否则按跨域处理。

document.getElementById('iframe').contentWindow.postMessage(data, "http://127.0.0.1:3000")

子页面:只有postMessage的目标源对应上,就会监听到传递过来的消息。

 window.addEventListener('message', (e)=> {       
    console.log(e.data,e,'second');
},false);


3.当点击了子页面的clickIframe方法,传递给父页面

子页面:使用window.parent方式传送消息给父页面

 window.parent.postMessage('子页面传到父页面的值', "http://127.0.0.1:3000/");

父页面: 只有postMessage的目标源对应上,就会监听到传递过来的消息。

 window.addEventListener('message', (e)=> {       
    console.log(e.data,e,'second');
},false);


postMessage ----open打开式使用方式

主页面。此页面既传递数据给子页面,又获取子页面的数据


被嵌入到主页面的js,此页面既传递数据给父页面,又获取父页面的数据

1。当点击了父页面的clickOpen方法,传递给子页面

父页面:此处获取的open的url要等于postMessage上的目标源。否则按跨域处理。

此处需要注意的是,一定要等子页面加载完毕,再去调用postMessage方法,否则,没有加载完的话,就没法触发监听事件了。

 let win = window.open("http://localhost:3001/")        
 setTimeout(()=>{            
    win.postMessage('我是父页面传到子页面的值open方式打开', "*")        
 }, 0)//必须等子页面搭建完成在发送

子页面:只有postMessage的目标源对应上,就会监听到传递过来的消息。

 window.addEventListener('message', (e)=> {       
    console.log(e.data,e,'second');
},false);


2.当点击了子页面的clickOpen方法,传递给父页面

子页面:使用window.opener方式传送消息给父页面

 window.opener.postMessage('子页面传到父页面的值', "http://127.0.0.1:3000/");

父页面: 只有postMessage的目标源对应上,就会监听到传递过来的消息。

 window.addEventListener('message', (e)=> {       
    console.log(e.data,e,'second');
},false);