Я пытаюсь обмениваться данными между поддоменами с помощью Safari. Я хотел бы использовать базу данных HTML5 (в частности, localStorage, так как мои данные представляют собой не что иное, как пары ключ-значение). Однако кажется, что данные, хранящиеся в domain.com, недоступны из sub.domain.com (или наоборот). Есть ли способ поделиться одной базой данных в этой ситуации?
Можно ли использовать базы данных HTML5 и localStorage между субдоменами?
Ответы (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.
Существует простой способ междоменного использования: просто создайте простую страницу, которая будет включена в качестве прокси-сервера 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
файл (вы также можете использовать свое имя файла и другой путь).
document.domain = superdomain
на обеих страницах, затем они можете взаимодействовать напрямую, а на главной странице вы просто используете iframe.contentWindow.localStorage
вместо window.localStorage
! Я думаю, это достаточно отличается, поэтому я опубликую его как отдельный ответ...
- person Doin; 26.08.2020
window.open
может работать, но для этого потребуется открыть и немедленно закрыть окно, которое может быть хорошо видно. Он также рискует блокировать всплывающие окна.
- person vishesh; 18.04.2021
Google Chrome блокирует доступ к localStoage из iFrame в другом домене по умолчанию, если только не включены сторонние файлы cookie, как и Safari на iPhone ... кажется, что единственным решением является открытие родительского домена в другом домене, а затем отправка в дочерний через window.postMessage, но на телефонах выглядит уродливо и неуклюже...
да. Вот как:
Для совместного использования субдоменов данного супердомена (например, 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 em> или 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 и т. д. непосредственно в коде вашего главного окна.
document.domain
ошибку? Доступ к iframe из родительского окна заблокирован? Или проблема возникает только при попытке использовать localStorage, например?
- person Doin; 18.04.2021
document.domain
устарел, он уже может не работать в некоторых браузерах. Проблема только тогда, когда iframe хочет получить доступ к своему локальному хранилищу.
- person vishesh; 18.04.2021
document.domain
? В любом случае, я с готовностью признаю, что есть ряд вещей, которые могут помешать этому решению. Однако, когда это работает, это может быть намного проще, чем другие решения на основе postMessage
, поскольку при его использовании вы получаете прямой доступ к API localStorage/indexedDB/etc.
- person Doin; 20.04.2021