最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • window.open() 打开的子页面往跨域的主页面传参问题

    正文概述 掘金(zachke)   2021-04-02   1453

    前言

    前段时间有遇到比较奇葩的需求,就是正常情况下,我们站点在进行第三方登录的时候,会通过 window.open 的方法,打开一个服务端的接口页面,然后这个服务端的接口去判断当前是否有授权行为,如果有的话,就写入 cookie,然后关闭当前窗体, 如果没有的话,就跳转到第三方的授权页面,然后在授权结束,回调的时候,写入 cookie,然后关闭当前窗体,前端会监听当前的窗体,当发现已经关闭的时候,就会去请求自动登录的接口,这时候,因为 cookie 信息已经让服务端写入了,所以这时候就可以正常登录,这时候前端还会读取服务端的某一个 token cookie, 然后生成当前一次性令牌 token, 以供服务端进行校验。

    正常情况下是没有问题的,但是由于 chrome 浏览器在新版本的时候,cookie的默认设置会有 SameSite=Lax,这时候就会导致前端读取不了服务端的 cookie (chrome 默认 cookie 的 SameSite=Lax,导致 http 模式的站点的第三方 cookie 无法进行跨域传输), 从而无法生成一次性访问 token 令牌, 这时候再去请求自动登录的接口,就会出现校验失败。

    所以这边的想法是,服务端在生成 cookie 的时候,能不能在窗口关闭之前,往前端传 当前的一次性令牌的 token。这样子前端就可以带上这个 token 去进行自动登录的校验。

    所以这边就会涉及到 window.open() 打开的子页面, 往主页面传参问题。

    前端模拟情况

    接下来我们用前端模拟一下这种情况,看能不能实现,我们知道 window.open 打开的新窗口,是可以获取到主页面的窗口句柄的,也就是 window.opener, 所以原则上是可以操作这个主页面的 window 对象。 接下来我们模拟两个页面:

    一个是主页面 parent.html:

    <!DOCTYPE>
    <html>
    <head>
    <meta charset="utf-8" />
    </script>
    </head>
    <body>
    <button id="btn">点我打开新窗口</button>
    <script>
    document.getElementById("btn").onclick = function(){
    	window.open('child.html',"新增","width=500,height=480,screenX=400,screenY=100");
    }
    // 子页面调用的函数
    window.triggerFn = function(data){
     alert("trigger by child page, data is:" + data);
    }
    </script>
    </body>
    </html>
    

    另一个是相同项目下的 child.html 页面:

    <!DOCTYPE">
    <html>
    <head>
    <meta charset="utf-8" />
    </script>
    </head>
    <body>
    <button id="btn">点我传递消息给主页面</button>
    <script>
    document.getElementById("btn").onclick = function(){
    	if (window.opener != null && !window.opener.closed) {
    		window.opener.triggerFn("hello");
    	}
    	window.close(true); 
    }
    </script>
    </body>
    </html>
    

    然后实践一下:

    window.open() 打开的子页面往跨域的主页面传参问题

    然后点击子界面的按钮,这时候就可以发现消息传到主界面,同时子界面关闭了

    window.open() 打开的子页面往跨域的主页面传参问题

    这时候发现子界面可以直接调用主界面的 window 对象(当然前提是主界面并没有关闭或者重新刷新)。

    看起来好像挺完美的, 接下来我将子界面换成另一个域名的路径,之前是同个站点的页面, 修改主界面的这个代码为

    window.open('http://127.0.0.1/test/child.html',"新增","width=500,height=480,screenX=400,screenY=100");
    // window.open('child.html',"新增","width=500,height=480,screenX=400,screenY=100");
    

    这是再试下, 这是就发现点击就没有任何响应了

    window.open() 打开的子页面往跨域的主页面传参问题

    看了一下控制台的报错,浏览器认为这个是跨域行为,因此被禁止了, 还是同源策略的原因。 第一次之所以可以,这个是因为两个页面是同源的。

    child.html:12 Uncaught DOMException: Blocked a frame with origin "http://127.0.0.1" from accessing a cross-origin frame.
        at HTMLButtonElement.document.getElementById.onclick (http://127.0.0.1/test/child.html:12:17)
    

    这时候通过断点可以看到,虽然子界面看似可以访问主界面的 window 对象,但是所有的操作都是报这个错:

    window.open() 打开的子页面往跨域的主页面传参问题

    所以如果把子页面改成服务端的页面也是一样的,因为都是跨域,而且就算服务端的页面在 response 的时候,加上 CORS 的跨域头,也是没有用。

    服务端模拟情况

    那么是不是真的无解呢, 跨域的情况下,看似子页面的 window.opener 对象虽然有,但是啥也干不了, 但是有一种情况还真可以干,那就是可以重写 window.opener.location.href 的值,这边涉及到一个 web 的安全问题: {% post_link a-target-blank %}, 这种看似安全缺陷的用法, 恰恰可以解决这个问题。

    接下来我们直接通过服务端的接口来模拟,client.html 是客户端页面,也就是主页面的代码:

    <!DOCTYPE>
    <html>
    <head>
    <meta charset="utf-8" />
    </script>
    </head>
    <body>
    <button id="btn">点我打开新窗口</button>
    <script>
    document.getElementById("btn").onclick = function(){
    	var win = window.open('http://127.0.0.1:3000/child/',"新增","width=500,height=480,screenX=400,screenY=100");
    	var t_q = setInterval(function () {
    		if (win.closed) {
    		    // child win is closed
    			clearInterval(t_q);
    		}
    	},200)
    }
    // 监听 hash 变化
    window.onhashchange = function(ev) {
    	// 获取新的 hash, 我这边是简单处理
    	var arr = document.location.hash.substring(1).split("=");
    	if(arr.length > 1 && arr[0] === "result"){
    		alert("服务端传的数据是:" + decodeURIComponent(arr[1]));
    	}
    }
    </script>
    
    </body>
    </html>
    

    然后服务端的代码如下:

    router.get('/child/', async (ctx, next) => {
        // 这边做一些很复杂的操作,从而得到 result 的值, 这边 sleep 2 s
        Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 2 * 1000);
        let resultJson = '{"result":"success","code":1}';
        ctx.body=`<script>
    window.opener.location.href = 'http://localhost/test/client.html#result=${resultJson}' ;
    window.close(); 
    window.opener=null
        </script>`;
    })
    

    服务端我用的是 nodejs 模拟的,用的是 koa 框架,而且为了表示正在处理逻辑,还做了一个 2s 的 sleep 操作。

    这边要注意一个细节,虽然可以重写主页面的 href , 但是为了不影响当前主页面的操作, 肯定是不能引起页面刷新的,所以这边重写的时候,要将传递的参数放在主页面的 hash 参数后面,这样子主页面就不会引起刷新, 同时主页面通过监听 window.onhashchange 事件来获取服务端传过来的 hash 值。

    具体操作截图如下,刚开始点击是这样子的:

    window.open() 打开的子页面往跨域的主页面传参问题

    然后过了 2s 之后,子页面重写主页面的 url,并且关闭窗口,然后主页面通过监听 hash 变化,取的 hash 值:

    window.open() 打开的子页面往跨域的主页面传参问题

    总结

    通过这种方式,就可以在用 window.open 打开跨域的页面的时候,往主页面传递参数, 当然这个参数不能太大。而且也适合服务端的接口的情况。


    更多好文请看我的个人站点: kebingzao.com/


    起源地下载网 » window.open() 打开的子页面往跨域的主页面传参问题

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元