我可以确定是否可以在没有 try/catch 的情况下将 postMessage 与 targetOrigin 一起使用吗?

Can I determine if I can use postMessage with a targetOrigin without try/catch?

提问人:Dai 提问时间:8/18/2023 最后编辑:Dai 更新时间:8/18/2023 访问量:53

问:

我正在实现 OIDC 的会话功能,该功能需要使用 在来自不同源的元素之间交换消息。window.postMessage<iframe>

我的 OIDC IdP 是 IdentityServer4。就此 QA 而言,我无法控制该页面。/connect/checksession

我的顶级页面的 HTML 如下所示:

<body>

    <!-- etc -->

    <iframe id="localCheckSessionIFrame" src="/local-check-session" loading="eager"></iframe>
    <iframe id="idpCheckSessionIFrame" src="https://example.com/connect/checksession" loading="eager"></iframe>

</body>

该文档有一个这样做的文档:/local-check-session<script>

var myClientId     = 'MyClientId';
var mySessionState = 'sQZ7Tyv9NYuMVwiydWMZetcetcetc...';

window.addEventListener( "message", onReceiveMessage, /*useCapture:*/ false );
// `function onReceiveMessage` omited for brevity.

checkSession();

var timerId = window.setInterval( checkSession, 5000 );

function checkSession() {

    const idpIFrame = window.top.document.getElementById( 'idpCheckSessionIFrame' );
    if( !idpIFrame ) return;
    idpIFrame.contentWindow.postMessage( /*message:*/ `${myClientId} ${mySessionState}`, /*targetOrigin: */ "https://example.com" );
}

  • 问题是当参数与 的实际 Origin 不匹配时,将失败(通过抛出 )。idpIFrame.contentWindow.postMessage(...)DOMExceptiontargetOrigin<iframe>
  • ...当顶级页面首次加载,就会发生这种情况:
    • <iframe id="localCheckSessionIFrame">将立即加载并运行。function checkSession()
    • ...但仍然会加载(这可能需要 2-5 秒) - 在加载之前,s Origin 将与 相同。<iframe id="idpCheckSessionIFrame">idpCheckSessionIFramelocalCheckSessionIFrame

我最初的解决方法是测试是否,但是这根本不起作用,因为如果跨源脚本尝试取消引用该属性,它们将抛出错误。if( idpIFrame.contentWindow.origin !== "https://example.com" ) { /*OK*/ }contentWindow.origin

// This doesn't work: dereferencing `contentWindow.origin` throws an error when idpIFrame is cross-origin.
if( idpIFrame.contentWindow.origin === "https://example.com" ) {
    idpIFrame.contentWindow.postMessage( message, /*targetOrigin: */ "https://example.com" );
}

我的第二次尝试是添加一个内联侦听器来设置一个标志,即: - 但是据我所知,对于跨域 iframe,根本不会调用侦听器。onload=""<iframe id="idpCheckSessionIFrame" onload="this.dataset['loaded'] = '1'">onload=""

到目前为止,我的第三个也是当前的解决方法是将调用包装在 a 中,如果尚未成功完成,则忽略异常,如下所示:idpIFrame.contentWindow.postMessagetry/catchpostMessage

var countPostMessageOK = 0;

function checkSession() {

    const idpIFrame = window.top.document.getElementById( 'idpCheckSessionIFrame' );
    if( !idpIFrame ) return;

    try {
        idpIFrame.contentWindow.postMessage( /*message:*/ `${myclientid} ${mysessionstate}`, /*targetOrigin: */ "https://example.com" );
        countPostMessageOK += 1;
    }
    catch( err ) {
        if( countPostMessageOK === 0 && ( err instanceof DOMException ) ) {
            // Ignore.
        }
        else {
            console.error( "postMessage failed: %o", err );
        }
    }
}

但这并不理想,因为异常仍在抛出,我担心可能会遇到其他错误,这些错误应该重新抛出或报告,而不是忽略。DOMException

另一种可能的解决方法是自定义 IdP 的页面,以便其他页面可以使用它首先确定页面最终加载(并最初忽略任何响应消息,因为这意味着我们还不能信任),然后在收到第一条响应消息后切换到指定实际结果并信任结果 - 但是, 目前,我无法自定义 - 但如果这是唯一的选择,我想我将不得不这样做(最终)。/connect/checksessiontargetOrigin: */connect/checksessiontargetOrigin: *<iframe>targetOrigin/connect/checksession<script>


有没有办法测试/检查 的 Origin 是否与调用匹配,而无需任何块?idpIFrame.contentWindowpostMessagetargetOriginpostMessagetry/catch

iframe 跨域 IdentityServer4 openid-connect 后消息

评论


答: 暂无答案