问题描述
我刚刚了解了 rel="noopener"
属性,它使打开的窗口不知道它们的开启器。我玩了一会儿,一直得到 window.opener === null
,直到我发现它现在是所有现代浏览器的新默认设置。
现在,我想实际阅读 window.opener
属性以了解其工作原理:
<!-- http://opener-from.test/ -->
<a href="http://opener-to.test/" target="_blank">no rel</a>
<a href="http://opener-to.test/" target="_blank" rel="opener">rel="opener"</a>
<a href="http://opener-to.test/" target="_blank" rel="noopener">rel="noopener"</a>
<!-- http://opener-to.test/ -->
<script>
console.log(window.opener);
</script>
no rel
和 rel="noopener"
链接只会使目的地获得 window.opener === null
。至于 rel="opener"
,它在 Firefox 和 Safari 中运行良好,但在 Google Chrome 中我收到错误:
未捕获的 DOMException:阻止了源为“http://opener-to.test”的框架访问跨源框架。
我在 Chrome 91 中对其进行了测试。如何在 Google Chrome 中的 window.opener
上下文中访问 target="_blank"
?
解决方法
这与默认的 rel
无关,它与跨域访问有关。来自MDN's documentation for opener
:
如果 opener 与当前页面不在同一来源,则 opener 对象的功能将受到限制。例如,窗口对象上的变量和函数是不可访问的。但是,打开器窗口的导航是可能的,这意味着打开的页面可以在原始选项卡或窗口中打开一个 URL。 >
(我的重点)
console.log(window.opener)
将尝试访问 window.opener
上的属性。
使用 rel="opener"
可以在 opener 中进行导航,但不能访问该窗口公开的全局变量(包括函数)。它还可以使用 postMessage
与 opener 窗口对话。
如果您只想查看是否可以访问 opener,请改为执行 console.log(window.opener === null)
并查看它是 true
(您没有访问权限)还是 false
(您有访问受限)。
尽管使用 rel="opener"
,但正如上面的 MDN 引用所示,它的访问权限有限。您无法获得对 window.opener
跨域(仅限同源)的完全访问权限。
这种仅允许有限访问对象内容的能力不仅仅是浏览器可以做到的魔法,您还可以通过 Proxy
在 JavaScript 本身中做到这一点:
const realOpenerWindow = {
x: 42,};
const opener = new Proxy(realOpenerWindow,{
get(target,property,receiver) {
if (allow) {
return Reflect.get(target,receiver);
}
throw new Error(`Access is disallowed`);
}
})
let allow = true;
console.log(opener === null); // false
console.log(opener.x); // 42
allow = false;
console.log(opener === null); // false
console.log(opener.x); // Throws error
关于为什么 Chrome 会出现错误,而 Firefox 等非 Chromium 浏览器不会:不同的控制台实现方式不同。显然 Firefox 的控制台是为了向您展示它允许向您展示的内容,但 Chrome 只是应用其通常的对象处理并触发访问错误。