我正在尝试使用Safari在子域之间共享数据。我想使用HTML5数据库(特别是 localStorage ,因为我的数据只是键值对)。但是,似乎无法从 sub.domain.com 访问存储到 domain.com 的数据(反之亦然)。在这种情况下,有没有办法共享一个数据库?
答案 0 :(得分:12)
Zendesk的library为我工作。
示例:
<强>集线器强>
// Config s.t. subdomains can get, but only the root domain can set and del
CrossStorageHub.init([
{origin: /\.example.com$/, allow: ['get']},
{origin: /:\/\/(www\.)?example.com$/, allow: ['get', 'set', 'del']}
]);
请注意$
以匹配字符串的结尾。上例中的正则表达式将匹配valid.example.com
等起源,但不匹配invalid.example.com.malicious.com
。
<强>客户端强>
var storage = new CrossStorageClient('https://store.example.com/hub.html');
storage.onConnect().then(function() {
return storage.set('newKey', 'foobar');
}).then(function() {
return storage.get('existingKey', 'newKey');
}).then(function(res) {
console.log(res.length); // 2
}).catch(function(err) {
// Handle error
});
答案 1 :(得分:9)
有一种简单的方法可以使用跨域名称,只需创建一个简单的页面,将其作为代理 iframe 包含在您尝试访问的域中,将 PostMessage 发送到那个iframe和内部iframe你做了LocalStorage数据库操作。这是article that do this with lcoalStorage的链接。这里是demo that send message to different page in subdomain检查源代码,它使用iframe和PostMessage。
答案 2 :(得分:4)
默认情况下,Google Chrome会阻止来自其他域中iFrame的localStoage访问,除非启用了第三方Cookie,因此iPhone上的Safari也是如此...唯一的解决方案似乎是打开其他域上的父域,然后发送到通过window.postMessage给孩子,但在手机上看起来很丑陋......
答案 3 :(得分:0)
要在给定超域的子域之间共享(例如foo.example.com与bar.example.com与example.com),可以在这种情况下使用一种技术。它可以应用于localStorage
,IndexedDB
,SharedWorker
,BroadcastChannel
等,所有这些元素在同源页面之间提供共享功能,但是由于某些原因不尊重对document.domain
的任何修改,这些修改将允许他们直接使用超域作为其起源。
(1)选取一个“主”域作为数据所属的域:即 https://foo.example.com 或 https://bar.example.com 或 https://example.com 将保存您的localStorage数据。假设您选择https://example.com。
(2)通常在所选域的页面上使用localStorage。
(3)在所有其他 https://*.example.com 页面( other 域)上,使用javascript设置document.domain = "example.com";
(始终超域)。然后还创建一个隐藏的<iframe>
,并将其导航到所选 https://example.com 域上的 some 页面( < 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>
如果编写用户脚本,则可能不希望将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
,window.indexedDB
等。所有这些对象的作用域将是选定的 https://example.com 原点-因此它们将具有您所有页面的共享来源相同!
此技术最尴尬的部分是,您必须等待iframe加载后才能继续。因此,例如,您不能仅仅在DOMContentLoaded处理程序中轻松地开始使用localStorage。另外,您可能需要添加一些错误处理,以检测隐藏的iframe是否无法正确加载。
很显然,您还应该确保在页面生命周期中未删除或导航隐藏的iframe ... OTOH我不知道这样做的结果,但是很可能会发生坏事。 / p>
而且,需要警告:可以使用document.domain
标头阻止设置/更改Feature-Policy
,在这种情况下,该技术将无法按所述使用。
但是,这种技术的概括要复杂得多,Feature-Policy
不能阻止它,并且允许完全不相关的域共享数据,通信和共享的工作器< / strong>(即不只是普通超级域的子域)。 @jcubic已经在回答中对此进行了描述,即:
总体思路是,如上所述,您创建了一个隐藏的iframe以提供正确的访问源;但是,您不仅可以直接获取iframe窗口的属性,还可以在iframe中使用脚本来完成所有工作,并且仅使用postMessage()
和addEventListener("message",...)
在iframe和主窗口之间进行通信。 / p>
之所以可行,是因为postMessage()
甚至可以在不同来源的窗口之间使用。但这也要复杂得多,因为您必须将所有内容都通过在iframe和主窗口之间创建的某种消息传递基础结构传递,而不是仅在主窗口的代码中直接使用localStorage,IndexedDB等API。>