跨子域使用 localStorage
Posted
技术标签:
【中文标题】跨子域使用 localStorage【英文标题】:use localStorage across subdomains 【发布时间】:2011-04-30 21:37:31 【问题描述】:我在可以支持它的浏览器上用localStorage 替换cookie(除了IE 之外的任何人)。问题在于 site.com 和 www。site.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)的子域之间的共享,您可以在这种情况下使用一种技术。它可以应用于localStorage
、IndexedDB
、SharedWorker
、BroadcastChannel
等,所有这些都提供了同源页面之间的共享功能,但由于某种原因,不尊重对document.domain
的任何修改会让他们直接使用超级域作为他们的来源。
(1) 选择一个“主”域作为数据所属的域:即 https://example.com 或 https://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.localStorage
、iframe.contentWindow.indexedDB
、iframe.contentWindow.BroadcastChannel
、iframe.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
,或通过<meta>
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 帐户进行跨子域跟踪。
使用 Express 和 Node,如何跨子域/主机头维护会话