【问题标题】:How to create dynamic iframe that works with postMessage如何创建与 postMessage 一起使用的动态 iframe
【发布时间】:2016-08-05 02:54:03
【问题描述】:

我正在开发一个项目,我们的网络应用需要通过 postMessage 协议与第三方应用集成。出于说明目的,假设这是代码,它工作得很好:

<html>

<head>

<script>
var ovsDomain = '3rd.party.vendor',
    ovsOrigin = 'https://' + ovsDomain,
    ovsApiUrl = ovsHost + '/webapi.html';

function begin() {
    var ovsFrame = document.getElementById( 'ovs' );

    if ( window.postMessage ) {
        if ( window.addEventListener ) {
            window.addEventListener( 'message', onMessage );
        } else {
            window.attachEvent( 'onmessage', onMessage );
        }
    }

    ovsFrame.src = ovsApiUrl + '#' + encodeURIComponent( document.location.href );
    ovsFrame.contentWindow.postMessage( JSON.stringify( {
        cmd:        'initialize',
        session:    'test_session'
    } ), ovsOrigin );
}

function post() {
    var ovsFrame = document.getElementById( 'ovs' ),
        msg = JSON.stringify( {
            cmd:    'send',
            data:   'test data'
        } );

    ovsFrame.contentWindow.postMessage( msg, ovsHost );
}

function end() {
    var ovsFrame = document.getElementById( 'ovs' );

    ovsFrame.contentWindow.postMessage( JSON.stringify( {
        cmd: 'close'
    } ), ovsOrigin );
}

function onMessage( evt ) {
    var data,
        ovsFrame = document.getElementById( 'ovs' );

    if ( evt.origin === ovsHost && ovsFrame.contentWindow === evt.source ) {

        try {
           data = JSON.parse(evt.data);
        } catch ( e ) {
           alert( 'bad data' );
        }

        switch ( data.msg ) {
        case 'close':
            alert( 'closed' );
            break;
        case 'initialized':
            alert( 'initialized' );
            break;
        }
   }
}

</script>

</head>

<body>
<input type='button' value='Begin' onclick='begin()' />
<input type='button' value='Post' onclick='post()' />
<input type='button' value='End' onclick='end()' />
<iframe id='ovs' sandbox='allow-scripts allow-same-origin allow-popups' src='https://3rd.party.vendor/webapi.html' style='display:none'></iframe>
</body>

</html>

我想根据以下条件有条件地创建/加载 iframe 元素:a) 是否启用了该功能;b) 我们使用的特定客户端和/或供应商 API 的 URL 应该是什么。所以我尝试从 HTML 文档中删除静态 iframe,并将以下调整添加到我的 begin 函数中:

function begin() {
    var ovsFrame = document.createElement( 'iframe' );

    if ( window.postMessage ) {
        if ( window.addEventListener ) {
            window.addEventListener( 'message', onMessage );
            ovsFrame.addEventListener( 'load', onFrameLoad );
        } else {
            window.attachEvent( 'onmessage', onMessage );
            ovsFrame.attachEvent( 'load', onFrameLoad );
        }
    }

    ovsFrame.id = 'crs';
    ovsFrame.src = ovsOrigin;
    document.body.appendChild( ovsFrame );
}

function onFrameLoad() {
    var ovsFrame = document.getElementById( 'ovs' );

    ovsFrame.src = ovsApiUrl + '#' + encodeURIComponent( document.location.href );
    ovsFrame.contentWindow.postMessage( JSON.stringify( {
        cmd:        'initialize',
        session:    'test_session'
    } ), ovsOrigin );
}

iframe 被创建并添加到 DOM。初始 URL 已加载,但 API 想要创建的弹出窗口被阻止。更重要的是,我的 postMessage 尝试被阻止:

在“DOMWindow”上执行“postMessage”失败:提供的目标源(“https://3rd.party.vendor”)与接收窗口的源(“https://althost.meditech.com:4433”)不匹配。

我的理解是,因为我在文档中创建了 iframe,所以它继承了我文档的域。完成后,我无法将其强制设置为 OV 的域,例如

ovsFrame.contentWindow.document.domain = ovsDomain;

由于OV域与我的无关。

对于我能找到的所有文档,没有任何地方明确指出你不能这样做(创建一个动态元素),尽管我没有找到一个例子,有人在 HTML 中没有静态 iframe 元素,所以它开始了感觉自己是我不知道的那些肮脏的小秘密(或传统智慧花絮)之一。

任何建议都将不胜感激,即使它确认无法完成。 我已经阅读了同源政策文档和

【问题讨论】:

  • 您是否尝试将sandbox 属性添加到动态创建的iframe 中? IOW,ovsFrame.sandbox = 'allow-scripts allow-same-origin allow-popups';,然后设置src?
  • 我没有,但这样做之后行为没有改变。
  • 我可以补充一下,将 postMessage 中的第二个参数更改为 '*' 将避免 postMessage 上的异常,尽管 OV 的弹出窗口仍然被阻止,并且在允许它之后,它似乎处于错误状态.我目前无法弄清楚静态 iframe 元素与动态 iframe 元素之间的区别。
  • 这可能是一个安全问题,因此恶意脚本无法开始通过脚本向您的应用添加 iframe。相反,他们必须在 HTML 到达浏览器之前拦截它...
  • 是的,我有点想它可能是这样的;修改 HTML 内容比使用 JS 注入更容易。即便如此,最好 a) 这样记录,b) 有办法动态更改 src(例如,如果我留下一个没有 src 或 src='' 的静态 iframe,允许稍后更改 src 并有交叉起源幸福)

标签: javascript html iframe postmessage


【解决方案1】:

我已确定解决此问题的方法,以防其他人遇到此问题并需要解决方案。动态创建 iframe 元素将继承脚本页面的来源;因为它是我的页面,所以我可以加载我托管的 HTML 文档,它将静态加载第 3 方 iframe。一旦我有了这个新页面,我就可以使用 window.postMessage 通过新页面与第 3 方页面进行通信,实际上是通过我的受信任来源代理通信。感觉有点笨拙,但听起来不错且有效。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-02-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-29
    • 2019-02-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多