跨子域使用localStorage

发布于 2024-09-28 19:28:42 字数 438 浏览 5 评论 0 原文

I'm replacing cookies with localStorage on browsers that can support it (anyone but IE). The problem is site.example and www.site.example store their own separate localStorage objects. I believe www is considered a subdomain (a stupid decision if you ask me). If a user was originally on site.example and decides to type in www.site.example on her next visit, all her personal data will be inaccessible. How do I get all my "subdomains" to share the same localStorage as the main domain?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

暮凉 2024-10-05 19:28:42

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

  • 使用父域中的 iframe - 比如说 parent.example
  • 然后在每个 child.example 域上,只需执行一个 postMessage 即可您的 parent.example iframe
  • 您所需要做的就是设置一个如何解释 postMessage 消息以与 parent.example iframe 对话的协议。

This is how I use it across domains...

  • Use an iframe from your parent domain - say parent.example
  • Then on each child.example domain, just do a postMessage to your parent.example iframe
  • All you need to do is setup a protocol of how to interpret your postMessage messages to talk to the parent.example iframe.
离旧人 2024-10-05 19:28:42

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

优点:

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

缺点:

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

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

If you're using the iframe and postMessage solution just for this particular problem, I think it might be less work (both code-wise and computation-wise) to just store the data in a subdomain-less cookie and, if it's not already in localStorage on load, grab it from the cookie.

Pros:

  • Doesn't need the extra iframe and postMessage set up.

Cons:

  • Will make the data available across all subdomains (not just www) so if you don't trust all the subdomains it may not work for you.
  • Will send the data to the server on each request. Not great, but depending on your scenario, maybe still less work than the iframe/postMessage solution.
  • If you're doing this, why not just use the cookies directly? Depends on your context.
  • 4K max cookie size, total across all cookies for the domain (Thanks to Blake for pointing this out in comments)

I agree with other commenters though, this seems like it should be a specifiable option for localStorage so work-arounds aren't required.

往日 2024-10-05 19:28:42

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

另外,请考虑使用跨浏览器解决方案,例如可以使用每个浏览器本机存储的 PersistJS

I suggest making site.example redirect to www.site.example for both consistency and for avoiding issues like this.

Also, consider using a cross-browser solution like PersistJS that can use each browser native storage.

野心澎湃 2024-10-05 19:28:42

在主域中设置cookie:

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

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

Set to cookie in the main domain:

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

and then take the data from any main domain or sub domain and set it on the localStorage

扎心 2024-10-05 19:28:42

方法如下:

[2020 年 11 月更新: 此解决方案依赖于能够设置 document.domain。不幸的是,这样做的能力现已被弃用。 另请注意,这样做会删除域和子域之间的“防火墙”,从而避免 XSS 攻击或其他恶意脚本的脆弱性,并对共享托管产生进一步的安全影响,如 MDN 页面 2022 年 9 月更新: 从 Chrome v109 开始,只能在同时发送 Origin-Agent-Cluster: ?0 标头的页面上设置 document.domain。< /em>]

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

(1) 选择一个数据所属的“主”域:即 https://example.com< /a>https://www.example.com将保存您的本地存储数据。假设您选择 https://example.com

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

(3) 在所有 https://www.example.com 页面上( 其他域),使用javascript设置document.domain = "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"));

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

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

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

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

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

并且,需要注意的是:可以使用 Feature-Policy 标头来阻止设置/更改 document.domain,在这种情况下,该技术将不会被使用。可以按照描述使用。


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

总体思路是,如上所述,您创建一个隐藏的 iframe 来提供正确的访问来源;但不是直接获取 iframe 窗口的属性,而是使用 iframe 内部的脚本来完成所有工作,并且仅使用 postMessage() 在 iframe 和主窗口之间进行通信>addEventListener("消息",...).

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

This is how:

[November 2020 Update: This solution relies on being able to set document.domain. The ability to do that has now been deprecated, unfortunately. NOTE ALSO that doing so removes the "firewall" between domains and subdomains for vulnerability to XSS attacks or other malicious script, and has further security implications for shared hosting, as described on the MDN page. September 2022 Update: From Chrome v109, setiing document.domain will only be possible on pages that also send an Origin-Agent-Cluster: ?0 header.]

For sharing between subdomains of a given superdomain (e.g. example.com), there's a technique you can use in that situation. It can be applied to localStorage, IndexedDB, SharedWorker, BroadcastChannel, etc, all of which offer shared functionality between same-origin pages, but for some reason don't respect any modification to document.domain that would let them use the superdomain as their origin directly.

(1) Pick one "main" domain to for the data to belong to: i.e. either https://example.com or https://www.example.com will hold your localStorage data. Let's say you pick https://example.com.

(2) Use localStorage normally for that chosen domain's pages.

(3) On all https://www.example.com pages (the other domain), use javascript to set document.domain = "example.com";. Then also create a hidden <iframe>, and navigate it to some page on the chosen https://example.com domain (It doesn't matter what page, as long as you can insert a very little snippet of javascript on there. If you're creating the site, just make an empty page specifically for this purpose. If you're writing an extension or a Greasemonkey-style userscript and so don't have any control over pages on the example.com server, just pick the most lightweight page you can find and insert your script into it. Some kind of "not found" page would probably be fine).

(4) The script on the hidden iframe page need only (a) set document.domain = "example.com";, and (b) notify the parent window when this is done. After that, the parent window can access the iframe window and all its objects without restriction! So the minimal iframe page is something like:

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

If writing a userscript, you might not want to add externally-accessible functions such as iframeReady() to your unsafeWindow, so instead a better way to notify the main window userscript might be to use a custom event:

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

Which you'd detect by adding a listener for the custom "iframeReady" event to your main page's window.

(NOTE: You need to set document.domain = "example.com" even if the iframe's domain is already example.com: Assigning a value to document.domain implicitly sets the origin's port to null, and both ports must match for the iframe and its parent to be considered same-origin. See the note here: https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy#Changing_origin)

(5) Once the hidden iframe has informed its parent window that it's ready, script in the parent window can just use iframe.contentWindow.localStorage, iframe.contentWindow.indexedDB, iframe.contentWindow.BroadcastChannel, iframe.contentWindow.SharedWorker instead of window.localStorage, window.indexedDB, etc. ...and all these objects will be scoped to the chosen https://example.com origin - so they'll have the this same shared origin for all of your pages!

The most awkward part of this technique is that you have to wait for the iframe to load before proceeding. So you can't just blithely start using localStorage in your DOMContentLoaded handler, for example. Also you might want to add some error handling to detect if the hidden iframe fails to load correctly.

Obviously, you should also make sure the hidden iframe is not removed or navigated during the lifetime of your page... OTOH I don't know what the result of that would be, but very likely bad things would happen.

And, a caveat: setting/changing document.domain can be blocked using the Feature-Policy header, in which case this technique will not be usable as described.


However, there is a significantly more-complicated generalization of this technique, that can't be blocked by Feature-Policy, and that also allows entirely unrelated domains to share data, communications, and shared workers (i.e. not just subdomains off a common superdomain). @Mayank Jain already described it in their answer, namely:

The general idea is that, just as above, you create a hidden iframe to provide the correct origin for access; but instead of then just grabbing the iframe window's properties directly, you use script inside the iframe to do all of the work, and you communicate between the iframe and your main window only using postMessage() and addEventListener("message",...).

This works because postMessage() can be used even between different-origin windows. But it's also significantly more complicated because you have to pass everything through some kind of messaging infrastructure that you create between the iframe and the main window, rather than just using the localStorage, IndexedDB, etc. APIs directly in your main window's code.

半枫 2024-10-05 19:28:42

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

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

I'm using xdLocalStorage, this is a lightweight js library which implements LocalStorage interface and support cross domain storage by using iframe post message communication.( angularJS support )

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

未蓝澄海的烟 2024-10-05 19:28:42

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

在服务器级别进行重定向

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

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


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

this kind of solution causes many problems like this. for consistency and SEO considerations
redirect on the main domain is the best solution.

do it redirection at the server level

How To Redirect www to Non-www with Nginx

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

or
any other level like route 53 if are using

夏日浅笑〃 2024-10-05 19:28:42

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

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

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

This is how I solved it for my website. I redirected all the pages without www to www.site.example. This way, it will always take localstorage of www.site.example

Add the following to your .htaccess, (create one if you already don't have it) in root directory

RewriteEngine On
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^(.*)$ http://www.%{HTTP_HOST}/$1 [R=301,L]
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文