我在浏览器上用 本地存储替换 cookie,这些浏览器可以支持 cookie (除了 IE)。问题是 site.example和 www.site.example存储它们各自的 localStorage 对象。我相信 www被认为是一个子域(如果你问我的话,这是一个愚蠢的决定)。如果用户最初使用的是 site.example,并决定在下次访问时键入 www.site.example,则无法访问其所有个人数据。如何让所有“子域”与主域共享相同的 localStorage?
site.example
www.site.example
www
我建议将 site.example重定向到 www.site.example,以保持一致性并避免类似的问题。
另外,考虑使用跨浏览器解决方案,如 坚持,它可以使用每个浏览器的本机存储。
这是我如何跨域使用它..。
parent.example
child.example
如果只针对这个特定问题使用 iframe 和 postMessage 解决方案,我认为仅使用 将数据存储在一个没有子域的 cookie 中,如果在加载时数据还没有存储在 localStorage 中,则从 cookie 中获取数据可能会减少工作量(无论是在代码方面还是在计算方面)。
不过,我同意其他评论者的看法,这似乎应该是 localStorage 的一个可指定的选项,因此不需要变通方法。
我使用的是 xdLocalStorage,这是一个轻量级的 js 库,它实现了 LocalStorage 接口,并通过使用 iframe post 消息通信支持跨域存储。(angularJS 支持)
Https://github.com/ofirdagan/cross-domain-local-storage
这就是我为我的网站解决它的方法。我将所有没有 www 的页面重定向到 www.site.example。这样,它总是采用 www.site.example的本地存储
将以下内容添加到根目录中的 .htaccess中(如果没有,则创建一个)
.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头文件的页面上进行。]
document.domain
Origin-Agent-Cluster: ?0
为了在给定超域的子域之间共享(例如 example.com) ,有一种技术可以在这种情况下使用。它可以应用于 localStorage,IndexedDB,SharedWorker,BroadcastChannel等,所有这些都提供了共享功能之间的同源网页,但由于某些原因,不尊重任何修改的 document.domain,让他们使用超域作为他们的直接来源。
localStorage
IndexedDB
SharedWorker
BroadcastChannel
(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服务器上的页面没有任何控制,只需选择您能找到的最轻量级的页面,并将您的脚本插入其中。某种类型的“未找到”页面可能是好的)。
document.domain = "example.com";
<iframe>
(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(),因此,通知主窗口用户脚本的更好方法可能是使用自定义事件:
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.localStorage,iframe.contentWindow.indexedDB,iframe.contentWindow.BroadcastChannel,iframe.contentWindow.SharedWorker而不是 window.localStorage,window.indexedDB等等... 所有这些对象将被限制在所选的 翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳 https://example.com原点范围内——所以它们将有你所有页面的相同的共享原点!
iframe.contentWindow.localStorage
iframe.contentWindow.indexedDB
iframe.contentWindow.BroadcastChannel
iframe.contentWindow.SharedWorker
window.localStorage
window.indexedDB
这项技术最棘手的部分是,在继续之前必须等待 iframe 加载。因此,例如,您不能在 DOMContentLoadedHandlder 中轻松地开始使用 localStorage。此外,您可能还需要添加一些错误处理来检测隐藏的 iframe 是否无法正确加载。
显然,您还应该确保隐藏的 iframe 在页面的生命周期内不会被删除或导航... ... OTOH 我不知道这会导致什么结果,但很可能会发生糟糕的事情。
并且,警告: 设置/更改 document.domain可以使用 Feature-Policy头文件为 被屏蔽了,在这种情况下,这种技术将不能如前所述使用。
Feature-Policy
然而,这种技术有一个显著更复杂的推广,它不能被 Feature-Policy阻止,也不能被 允许完全不相关的域共享数据、通信和共享工作者阻止(即不仅仅是一个公共超域的子域)。@ Mayank Jain 在他们的回答中已经描述过了,即:
总体思路是,就像上面一样,你创建一个隐藏的 iframe 来提供正确的访问原点; 但是不仅仅是直接获取 iframe 窗口的属性,你使用 iframe 内部的脚本来完成所有的工作,你只使用 postMessage()和 addEventListener("message",...)在 iframe 和主窗口之间进行通信。
postMessage()
addEventListener("message",...)
这是因为即使在不同原点的窗口之间也可以使用 postMessage()。但是它也非常复杂,因为您必须通过在 iframe 和主窗口之间创建的某种类型的消息传递基础结构传递所有内容,而不是仅仅使用 localStorage、 IndexedDB 等。直接位于主窗口代码中的 API。