跨子域使用 localStorage

我在浏览器上用 本地存储替换 cookie,这些浏览器可以支持 cookie (除了 IE)。问题是 site.examplewww.site.example存储它们各自的 localStorage 对象。我相信 www被认为是一个子域(如果你问我的话,这是一个愚蠢的决定)。如果用户最初使用的是 site.example,并决定在下次访问时键入 www.site.example,则无法访问其所有个人数据。如何让所有“子域”与主域共享相同的 localStorage?

159815 次浏览

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

另外,考虑使用跨浏览器解决方案,如 坚持,它可以使用每个浏览器的本机存储。

这是我如何跨域使用它..。

  • 使用父域中的 iframe-比如 parent.example
  • 然后,在每个 child.example域上,只需向您的 parent.example iframe 发送一个 postMessage
  • 您所需要做的就是设置一个协议,说明如何解释您的 postMessage 消息以便与 parent.example iframe 交谈。

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

优点:

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

缺点:

  • 将使数据可用于所有子域(不仅仅是 www) ,所以如果你不信任所有的子域它可能不适合你。
  • 将在每次请求时将数据发送到服务器。不是很好,但是根据您的场景,可能仍然比 iframe/postMessage 解决方案少工作。
  • 如果您正在这样做,为什么不直接使用 cookie 呢? 这取决于您的上下文。
  • 4K 的最大 Cookie 大小,总共跨域 Cookie (感谢 Blake 在评论中指出这一点)

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

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

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

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

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

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

在主域中设置为 cookie:

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

然后从任何主域或子域获取数据并将其设置为 localStorage

这种解决方案会导致许多像这样的问题。为了一致性和搜索引擎优化的考虑 在主域上重定向是最好的解决方案。

在服务器级进行重定向

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

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

或者 任何其他楼层,例如53号公路

这就是方法:

[ 2020年11月更新: 这个解决方案依赖于能够设置 document.domain。不幸的是,这种能力现在已经被否定了。这样做可以移除域和子域之间的“防火墙”,以防止受到 XSS 攻击或其他恶意脚本的攻击,并且对共享主机具有进一步的安全影响,正如在 < a href = “ https://developer.mozilla.org/en-US/docs/Web/API/Document/domain # depreation”rel = “ nofollow noReferrer”> MDN 页面 上描述的那样。 2022年9月更新: 在 Chromev109中,设置 ABC0只能在同时发送 Origin-Agent-Cluster: ?0头文件的页面上进行。]

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

(1)为所属数据选择一个“ main”域: 即 翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳 https://example.com翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳 https://www.example.com将保存 localStorage 数据。假设你选择 https://example.com

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

(3)在所有 翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳 https://www.example.com页面(其他域)上,使用 javascript 设置 document.domain = "example.com";。然后创建一个隐藏的 <iframe>,并将其导航到所选的 翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳 https://example.com域(不要紧 < em > 什么 页面)上的 一些页面,只要您可以在其中插入一小段 javascript。如果您正在创建站点,只需要为此目的创建一个空白页面即可。如果您正在编写一个扩展或 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>

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

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

通过将自定义“ iframe Ready”事件的侦听器添加到主页面的窗口,可以检测到这一点。

(注意: 即使 iframe 的域已经是 Example.com,您也需要设置 document.domain = “ example.com”: 将一个值赋给 document.domain 隐式地将原点的 左舷设置为 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.localStoragewindow.indexedDB等等... 所有这些对象将被限制在所选的 翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳 https://example.com原点范围内——所以它们将有你所有页面的相同的共享原点!

这项技术最棘手的部分是,在继续之前必须等待 iframe 加载。因此,例如,您不能在 DOMContentLoadedHandlder 中轻松地开始使用 localStorage。此外,您可能还需要添加一些错误处理来检测隐藏的 iframe 是否无法正确加载。

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

并且,警告: 设置/更改 document.domain可以使用 Feature-Policy头文件为 被屏蔽了,在这种情况下,这种技术将不能如前所述使用。


然而,这种技术有一个显著更复杂的推广,它不能被 Feature-Policy阻止,也不能被 允许完全不相关的域共享数据、通信和共享工作者阻止(即不仅仅是一个公共超域的子域)。@ Mayank Jain 在他们的回答中已经描述过了,即:

总体思路是,就像上面一样,你创建一个隐藏的 iframe 来提供正确的访问原点; 但是不仅仅是直接获取 iframe 窗口的属性,你使用 iframe 内部的脚本来完成所有的工作,你只使用 postMessage()addEventListener("message",...)在 iframe 和主窗口之间进行通信。

这是因为即使在不同原点的窗口之间也可以使用 postMessage()。但是它也非常复杂,因为您必须通过在 iframe 和主窗口之间创建的某种类型的消息传递基础结构传递所有内容,而不是仅仅使用 localStorage、 IndexedDB 等。直接位于主窗口代码中的 API。