Можно ли использовать базы данных HTML5 и localStorage между субдоменами?

Я пытаюсь обмениваться данными между поддоменами с помощью Safari. Я хотел бы использовать базу данных HTML5 (в частности, localStorage, так как мои данные представляют собой не что иное, как пары ключ-значение). Однако кажется, что данные, хранящиеся в domain.com, недоступны из sub.domain.com (или наоборот). Есть ли способ поделиться одной базой данных в этой ситуации?


person Sebastian Celis    schedule 14.11.2010    source источник
comment
Возможный дубликат Сохранять переменные javascript на разных страницах?   -  person    schedule 16.04.2018


Ответы (4)


Обновление 2016

Мне подошла эта библиотека от Zendesk.

Образец:

Центр

// 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
});

Проверьте https://stackoverflow.com/a/39788742/5064633.

person super1ha1    schedule 30.09.2016

Существует простой способ междоменного использования: просто создайте простую страницу, которая будет включена в качестве прокси-сервера iframe, размещенного на домене, к которому вы пытаетесь получить доступ, отправьте PostMessage в этот iframe и внутри iframe вы выполняете манипуляции с базой данных LocalStorage. Вот ссылка на статью, в которой это делается с помощью lcoalStorage. . А вот демонстрация, которая отправляет сообщение на другую страницу в поддомене, проверьте исходный код, он использует iframe и PostMessage .

EDIT: новая версия библиотеки sysend.js (используется выше демо) используйте BroadcastChannel, если браузер его поддерживает, но все равно требует Iframe. Последняя версия также упрощает использование сообщений Cross-Origin, у вас есть html iframe в репо, который вы можете использовать (или вы можете использовать простой html-файл с одним тегом script с lib), а в родительском вам просто нужно вызвать одну функцию sysend.proxy('https://example.com'); где example.com должен иметь proxy.html файл (вы также можете использовать свое имя файла и другой путь).

person jcubic    schedule 22.06.2014
comment
Это только у меня или такое вонючее решение? Звучит нелепо. (Нет отрицательных голосов ... просто говорю). - person Rap; 16.09.2014
comment
На самом деле это не бесполезно, так как PostMessage был создан именно для связи между доменами. Проблема заключается в сохранении/поддержании содержимого IFRAME на разных хостах. Скажем, вы используете стороннее решение для корзины покупок… Не всегда возможно создать целевую страницу на их домене для вашего PostMessage, получающего IFRAME. Кроме того, обратите внимание на новый обмен сообщениями канала, который является двунаправленным (поддержка начинается в IE10. ). - person thirdender; 16.12.2015
comment
Наверное лучшее решение этой темы! Родной и эффективный. - person Julien Moulin; 27.12.2017
comment
Это лучшее общее решение для разнородных доменов. НО, когда у вас есть только поддомены, как у OP, есть НАМНОГО более простое решение: вы все еще используете прокси-iframe для хранения правильного источника для доступа к данным, но вместо postMessage() вы просто устанавливаете document.domain = superdomain на обеих страницах, затем они можете взаимодействовать напрямую, а на главной странице вы просто используете iframe.contentWindow.localStorage вместо window.localStorage! Я думаю, это достаточно отличается, поэтому я опубликую его как отдельный ответ... - person Doin; 26.08.2020
comment
Это решение не будет работать, если в браузере включен параметр «Блокировать сторонние файлы cookie». Он включен по умолчанию в сафари. - person vishesh; 17.04.2021
comment
@vishesh знаете ли вы какой-либо другой способ отправить междоменное сообщение в Safari, похоже, ничего не сработает, если только вы не прокси через сервер. - person jcubic; 17.04.2021
comment
В междоменном обмене сообщениями нет проблем, проблема заключается в получении данных сообщения из локального хранилища в iframe. op хочет специально читать данные из локального хранилища. window.open может работать, но для этого потребуется открыть и немедленно закрыть окно, которое может быть хорошо видно. Он также рискует блокировать всплывающие окна. - person vishesh; 18.04.2021
comment
@vishesh обратите внимание, что когда ответ был создан, решение работало в Iframe в Safari, а не так, как любой другой ответ. Кажется, что Safari блокирует любой способ отправки данных внутри iframe, может быть, на тот случай, если рекламодатели захотят создать обходной путь. Я думаю, что это должно работать только с файлами cookie и новыми FLoC. - person jcubic; 19.04.2021

Google Chrome блокирует доступ к localStoage из iFrame в другом домене по умолчанию, если только не включены сторонние файлы cookie, как и Safari на iPhone ... кажется, что единственным решением является открытие родительского домена в другом домене, а затем отправка в дочерний через window.postMessage, но на телефонах выглядит уродливо и неуклюже...

person Mad Scientist    schedule 21.02.2016
comment
Есть ли в Chrome настройка для изменения этого поведения? - person Manish Jain; 26.10.2017

да. Вот как:

Для совместного использования субдоменов данного супердомена (например, foo.example.com, bar.example.com и example.com) есть техника, которую вы можете использовать в этой ситуации. Его можно применить к localStorage, IndexedDB, SharedWorker, BroadcastChannel и т. д., все из которых предлагают общие функции для страниц одного происхождения, но по какой-то причине не учитывают никаких изменений в document.domain, которые позволили бы им напрямую использовать супердомен в качестве источника. .

ПРИМЕЧАНИЕ. Этот метод зависит от настройки document.domain, чтобы разрешить прямую связь между фреймами iframe в разных субдоменах. Сейчас эта функция устарела. (Хотя по состоянию на апрель 2021 года он продолжает работать во всех основных браузерах и, возможно, будет сохранен для обратной совместимости).

(1) Выберите один основной домен, которому будут принадлежать данные: например, https://foo.example.com или https://bar.example.com или https://example.com будут хранить ваши данные localStorage. Допустим, вы выбрали https://example.com.

(2) Используйте localStorage как обычно для страниц выбранного домена.

(3) На всех остальных страницах https://*.example.com (другие домены) используйте javascript для установки document.domain = "example.com"; (всегда супердомен). Затем также создайте скрытый <iframe> и перейдите на некоторую страницу на выбранном https://example.com (не имеет значения, какая страница, если вы можете вставить туда очень маленький фрагмент кода javascript. Если вы создаете сайт, просто создайте пустую страницу специально для этой цели. Если вы пишете расширение или пользовательский скрипт в стиле Greasemonkey и поэтому не имеете никакого контроля над страницами на example.com< /em> server, просто выберите самую легкую страницу, которую вы можете найти, и вставьте в нее свой скрипт.

(4) Сценарию на скрытой странице iframe нужно только (а) установить document.domain = "example.com"; и (б) уведомить родительское окно, когда это будет сделано. После этого родительское окно может без ограничений обращаться к окну 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 в окно вашей главной страницы.

(ПРИМЕЧАНИЕ: вам нужно установить document.domain = example.com, даже если домен iframe уже 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, прежде чем продолжить. Таким образом, вы не можете просто так начать использовать localStorage, например, в обработчике DOMContentLoaded. Также вы можете добавить некоторую обработку ошибок, чтобы определить, правильно ли загружается скрытый iframe.

Очевидно, вы также должны убедиться, что скрытый iframe не удаляется и не перемещается в течение всего срока службы вашей страницы... OTOH Я не знаю, к чему это приведет, но, скорее всего, произойдут плохие вещи.

И предостережение: установка/изменение document.domain может быть заблокирована с помощью заголовка Feature-Policy, и в этом случае этот метод нельзя будет использовать, как описано.


Однако существует значительно более сложное обобщение этой техники, которое не может быть заблокировано Feature-Policy и которое также позволяет совершенно несвязанным доменам обмениваться данными, сообщениями и общими работниками (т. е. не только субдомены вне общего супердомена). @jcubic уже описал это в своем ответе, а именно:

Общая идея заключается в том, что, как и выше, вы создаете скрытый iframe, чтобы указать правильное происхождение для доступа; но вместо того, чтобы просто захватить свойства окна iframe напрямую, вы используете скрипт внутри iframe для выполнения всей работы, и вы взаимодействуете между iframe и вашим главным окном только с помощью postMessage() и addEventListener("message",...).

Это работает, потому что postMessage() можно использовать даже между окнами разного происхождения. Но это также значительно сложнее, потому что вам нужно передавать все через какую-то инфраструктуру обмена сообщениями, которую вы создаете между iframe и главным окном, а не просто использовать API localStorage, IndexedDB и т. д. непосредственно в коде вашего главного окна.

person Doin    schedule 26.08.2020
comment
Это решение не будет работать, если в браузере включен параметр «Блокировать сторонние файлы cookie». Он включен по умолчанию в сафари. - person vishesh; 17.04.2021
comment
@vishesh Этот ответ не включает файлы cookie как таковые, однако вполне вероятно, что настройка Safari блокирует и другие вещи. Было бы неплохо иметь больше информации. Что именно не работает? Выдает ли параметр document.domain ошибку? Доступ к iframe из родительского окна заблокирован? Или проблема возникает только при попытке использовать localStorage, например? - person Doin; 18.04.2021
comment
Да, браузеры отключают доступ к локальному хранилищу так же, как и к файлам cookie. Это контролируется той же настройкой «Блокировать сторонние файлы cookie». document.domain устарел, он уже может не работать в некоторых браузерах. Проблема только тогда, когда iframe хочет получить доступ к своему локальному хранилищу. - person vishesh; 18.04.2021
comment
@vihesh Что ж, если браузер блокирует доступ iframe даже к своему собственному локальному хранилищу, нет особого смысла спрашивать, можете ли вы предоставить доступ к нему через поддомены. ...Или это только фреймы, а не окна верхнего уровня? Или это происходит только при изменении document.domain? В любом случае, я с готовностью признаю, что есть ряд вещей, которые могут помешать этому решению. Однако, когда это работает, это может быть намного проще, чем другие решения на основе postMessage, поскольку при его использовании вы получаете прямой доступ к API localStorage/indexedDB/etc. - person Doin; 20.04.2021
comment
@vihesh Я успешно использую его в скрипте Greasemonkey, работающем в Firefox, кстати. - person Doin; 20.04.2021
comment
@vihesh Если параметры безопасности, о которых вы говорите, относятся к встроенным iframe, можете ли вы обойти это, открыв новое окно (вместо использования iframe)? Это уродливо в том, что новое окно будет видно пользователю, и вам придется доверять ему, чтобы он не закрыл или не ушел (или вы могли обнаружить это и снова открыть его, при условии, что блокировщик всплывающих окон не понял)... но в некоторых случаях это может быть приемлемым решением...? - person Doin; 20.04.2021