跨子域使用 localStorage

Posted

技术标签:

【中文标题】跨子域使用 localStorage【英文标题】:use localStorage across subdomains 【发布时间】:2011-04-30 21:37:31 【问题描述】:

我在可以支持它的浏览器上用localStorage 替换cookie(除了IE 之外的任何人)。问题在于 site.comwwwsite.com 存储各自独立的 localStorage 对象。我相信 www 被认为是一个子域(如果你问我,这是一个愚蠢的决定)。如果用户最初访问 site.com 并决定在下次访问时输入 www.site.com,她的所有个人数据将无法访问。如何让我的所有“子域”与主域共享同一个 localStorage?

【问题讨论】:

Firefox 和 IE8 支持在用户指定的域下存储持久数据。例如,在 FF 上,您可以执行 globalStorage['site.com'],这将适用于 www.site.com 和 site.com。我还没有弄清楚如何在 Chrome 的实现中做到这一点。 考虑使用其中一个或后者——重定向所有使用 www 访问的用户。子域到无子域的域,或者相反。 我很久以前写过文章:Cross-Domain LocalStorage 【参考方案1】:

方法如下:

[2020 年 11 月更新: 此解决方案依赖于能够设置 document.domain。不幸的是,这样做的能力现在已被弃用。]

对于给定超级域(例如 example.com)的子域之间的共享,您可以在这种情况下使用一种技术。它可以应用于localStorageIndexedDBSharedWorkerBroadcastChannel 等,所有这些都提供了同源页面之间的共享功能,但由于某种原因,不尊重对document.domain 的任何修改会让他们直接使用超级域作为他们的来源。

(1) 选择一个“主”域作为数据所属的域:即 https://example.comhttps://www.example.com 将保存您的 localStorage 数据。假设您选择https://example.com。

(2) 对所选域的页面正常使用 localStorage。

(3) 在所有 https://www.example.com 页面(other 域)上,使用 javascript 设置document.domain = "example.com";。然后还创建一个隐藏的<iframe>,并将其导航到所选https://example.com 域上的一些 页面(没关系什么 页面,只要您可以在其中插入少量的 javascript sn-p。如果您正在创建站点,只需为此目的专门制作一个空页面即可。扩展程序或 Greasemonkey 样式的用户脚本,因此无法控制 example.com 服务器上的页面,只需选择您能找到的最轻量级的页面并将脚本插入其中即可。某种“未找到”页面可能会很好)。

(4) 隐藏 iframe 页面上的脚本只需要 (a) 设置 document.domain = "example.com";,并且 (b) 完成后通知父窗口。之后,父窗口可以不受限制地访问 iframe 窗口及其所有对象!所以最小的 iframe 页面是这样的:

<!doctype html>
<html>
<head>
  <script>
    document.domain = "example.com";
    window.parent.iframeReady();  // function defined & called on parent window
  </script>
</head>
<body></body>
</html>

如果编写用户脚本,您可能不想将iframeReady() 等外部可访问函数添加到unsafeWindow,因此通知主窗口用户脚本的更好方法可能是使用自定义事件:

    window.parent.dispatchEvent(new CustomEvent("iframeReady"));

您可以通过将自定义“iframeReady”事件的侦听器添加到您的主页窗口来检测。

(注意:即使 iframe 的域已经是 example.com,您也需要设置 document.domain = "example.com":为 document.domain 分配值会隐式设置源的 port 为 null,并且两个端口必须匹配 iframe 及其父级才能被视为同源。请参阅此处的注释:https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy#Changing_origin)

(5) 一旦隐藏的 iframe 通知其父窗口它已准备好,父窗口中的脚本可以使用 iframe.contentWindow.localStorageiframe.contentWindow.indexedDBiframe.contentWindow.BroadcastChanneliframe.contentWindow.SharedWorker 而不是 window.localStorage、@987654346 @ 等...所有这些对象的范围都将限定为所选的 https://example.com 来源 - 因此它们将为您的所有页面拥有相同的共享来源!

此技术最尴尬的部分是您必须等待 iframe 加载后才能继续。因此,例如,您不能只是愉快地开始在 DOMContentLoaded 处理程序中使用 localStorage。此外,您可能需要添加一些错误处理来检测隐藏的 iframe 是否无法正确加载。

显然,您还应该确保隐藏的 iframe 在页面的生命周期内没有被删除或导航... OTOH 我不知道结果会是什么,但很可能会发生不好的事情。

并且,需要注意的是:设置/更改document.domain 可以使用Feature-Policy 标头阻止,在这种情况下,该技术将无法如所述使用。


但是,这种技术有一个更为复杂的概括,Feature-Policy 无法阻止它,而且它还允许完全不相关的域共享数据、通信和共享工作人员(即不仅仅是公共超级域的子域)。 @Mayank Jain 已经在他们的回答中描述了它,即:

一般的想法是,就像上面一样,您创建一个隐藏的 iframe 来提供正确的访问来源;但不是直接获取 iframe 窗口的属性,而是使用 iframe 内的脚本来完成所有工作,并且只使用 postMessage()addEventListener("message",...) 在 iframe 和主窗口之间进行通信。

这是因为postMessage() 甚至可以在不同来源的窗口之间使用。但它也明显更加复杂,因为您必须通过在 iframe 和主窗口之间创建的某种消息传递基础架构传递所有内容,而不是直接在主窗口的代码中使用 localStorage、IndexedDB 等 API。

【讨论】:

【参考方案2】:

这种解决方案会导致很多这样的问题。出于一致性和 SEO 考虑 在主域上重定向是最好的解决方案。

在服务器级别进行重定向

如何使用 nginx 将 www 重定向到非 www

https://www.digitalocean.com/community/tutorials/how-to-redirect-www-to-non-www-with-nginx-on-centos-7

任何其他级别,如 53 号路线(如果正在使用)

【讨论】:

【参考方案3】:

设置为主域中的cookie -

document.cookie = "key=value;domain=.mydomain.com"

然后从任意主域或子域中取出数据,并设置在localStorage上

【讨论】:

那么,所有的post请求都会被CSRF攻击..【参考方案4】:

我建议将 site.com 重定向到 www.site.com 以保持一致性并避免此类问题。

另外,请考虑使用像 PersistJS 这样的跨浏览器解决方案,它可以使用每个浏览器的原生存储。

【讨论】:

我没有对服务器执行此类重定向的管理员访问权限。该库是否允许我在 www 和非 www 之间共享持久数据?经过一番阅读,似乎几乎所有浏览器的存储机制都不允许这样做。不管是cookies还是localStorage,都会遇到这个问题…… 是的,存储通常依赖于域,包括子域。这就是我建议重定向的原因。您不一定需要管理员权限,只需在文档根目录中使用 .htaccess 规则 @JoJo 有几种重定向方式,例如通过发送标头Location,或通过&lt;meta&gt; HTML 标记,甚至通过window.location 发送JS。 这只是在回避答案。看到 Mayank 的回答是正确的。 +1 @avoiding,再加上这与其他情况无关 - 比如我在这里 lang1.domain.com - lang2.domain.com【参考方案5】:

这就是我为我的网站解决它的方法。我将所有没有 www 的页面重定向到 www.site.com。这样,它将始终占用 www.site.com 的本地存储

将以下内容添加到您的 .htacess,(如果您还没有,则创建一个)在根目录中

RewriteEngine On
RewriteCond %HTTP_HOST !^www\. [NC]
RewriteRule ^(.*)$ http://www.%HTTP_HOST/$1 [R=301,L]

【讨论】:

我很想对此投反对票,但我不会因为它可以帮助 OP 的用例,但对于想要在 myapp.com 和 developers.myapp.com 上保持会话并支持的人来说.myapp.com,这个答案不好。 嘿@DonOmondi 如果你能帮助我提供你所建议的链接,我将不胜感激! OP 询问“跨子域使用 localStorage”,您的答案是“将 www 重定向到非 www”非常不同,但当且仅当特定子域为“www.abc.com”时它才能工作对于一般情况,这里的其他一些答案更实用。【参考方案6】:

我用的是xdLocalStorage,这是一个轻量级的js库,它实现了LocalStorage接口,通过iframe post消息通信支持跨域存储。(angularJS支持)

https://github.com/ofirdagan/cross-domain-local-storage

【讨论】:

我看过了,但它似乎在 Safari 上不起作用。 github.com/ofirdagan/cross-domain-local-storage/issues/10【参考方案7】:

如果您只是针对这个特定问题使用 iframe 和 postMessage 解决方案,我认为将数据存储在无子域的 cookie 中可能会减少工作量(无论是在代码方面还是在计算方面)并且,如果加载时它尚未在 localStorage 中,请从 cookie 中获取它

优点:

不需要额外的 iframe 和 postMessage 设置。

缺点:

将使数据在所有子域(不仅仅是 www)中可用,因此如果您不信任所有子域,它可能对您不起作用。 将在每次请求时将数据发送到服务器。不是很好,但根据您的情况,可能仍然比 iframe/postMessage 解决方案少。 如果您这样做,为什么不直接使用 cookie?取决于您的上下文。 4K 最大 cookie 大小,域中所有 cookie 的总和(感谢 Blake 在 cmets 中指出这一点)

我同意其他评论者的观点,这似乎应该是 localStorage 的可指定选项,因此不需要变通方法。

【讨论】:

缺点:最大 4k cookie 大小 另外,正如我所学的那样,4k 限制是针对单个域的所有 cookie 大小的总和,而不是针对每个 cookie。 其他缺点: - cookie 更有可能被广告拦截器阻止 - cookie 旨在用于在服务器和客户端之间共享小数据,如果服务器不使用您存储在 cookie 中的数据,因此,这是一种滥用 还有一个缺点:在 Safari 和 Brave 等浏览器上,从前端设置的 cookie 的最长有效期为 7 天。【参考方案8】:

这就是我跨域使用它的方式...

使用父域中的 iframe - 例如 parent.com 然后在每个 child.com 域上,向您的 parent.com iframe 发送 postMessage 您需要做的就是设置一个协议,说明如何解释您的 postMessage 消息以与 parent.com iframe 对话。

希望对你有帮助:)

【讨论】:

这是真正的答案,而不是勾选的答案。我自己完成了这项工作,但也使用 postMessage 创建了一个方便的回调包装器。 这是一篇很好的文章,其中包含一些解释此方法的示例代码:jcubic.wordpress.com/2014/06/20/cross-domain-localstorage 请注意,这只有在第三方 cookie 未被禁用时才有可能:***.com/a/44097269/4311428 Apple 已更新桌面和移动设备上 Safari 7+ 的默认设置,以阻止第三方数据。该选项现在称为“阻止 cookie 和其他网站数据”,它指的是现在完全被域隔离的 localstorage 之类的东西。此方法在 Safari 中不起作用 @Max @Aranganathan 对于原始问题案例仍然有效 - site.com/www.site.com 只要子域位于同一个父域上

以上是关于跨子域使用 localStorage的主要内容,如果未能解决你的问题,请参考以下文章

使用 Google Analytics 对子域进行跨域跟踪

使用多个 Google Analytics 帐户进行跨子域跟踪。

使用 Express 和 Node,如何跨子域/主机头维护会话

Firebase - 使用 firebase admin sdk 跨子域共享身份验证

链接预取是不是跨子域工作?

有没有办法让一个人在跨子域的 firebase 上进行身份验证