繁体   English   中英

跨子域使用 localStorage

[英]Use localStorage across subdomains

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

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

  • 使用父域中的 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 的可指定选项,因此不需要变通方法。

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

此外,考虑使用可以使用每个浏览器原生存储的跨浏览器解决方案,例如PersistJS

在主域中设置 cookie:

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

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

这是如何:

[ 2020 年 11 月更新:此解决方案依赖于能够设置document.domain 不幸的是,这样做的能力现在已被弃用。 注意 ALSO这样做可以消除域和子域之间的“防火墙”,以免受到 XSS 攻击或其他恶意脚本的攻击,并且对共享主机有进一步的安全影响,如MDN 页面所述。 ]

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

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

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

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

(4) 隐藏 iframe 页面上的脚本只需要 (a) set 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 赋值会隐式将源端口设置为 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 加载后才能继续。 因此,例如,您不能只是愉快地开始在 DOMContentLoaded 处理程序中使用 localStorage。 此外,您可能希望添加一些错误处理来检测隐藏的 iframe 是否无法正确加载。

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

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


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

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

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

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

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

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

在服务器级别进行重定向

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

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

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

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

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

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

您可以只使用document.domain属性。 如果将其放在JavaScript的第一个动作中:

document.domain="mysite.com";

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM