Вот как:
[ Обновление за ноябрь 2020 г .: это решение зависит от возможности установить document.domain
. К сожалению, такая возможность устарела. ]
Для обмена между поддоменами данного супердомена (например, example.com) есть метод, который вы можете использовать в этой ситуации. Он может быть применен к localStorage
, IndexedDB
,SharedWorker
, BroadcastChannel
и т.д., все из которых имеют общую функциональность между же происхождения страниц, но по какой - то причине не соблюдают никаких изменений, document.domain
которые позволили бы им использовать супердомена как их происхождение непосредственно.
(1) Выберите один «основной» домен, к которому будут принадлежать данные: например, 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 ( неважно, на какой странице , если вы можете вставить туда очень маленький фрагмент javascript. Если вы при повторном создании сайта просто создайте пустую страницу специально для этой цели. Если вы пишете расширение или пользовательский скрипт в стиле Greasemonkey и не имеете никакого контроля над страницами на example.comserver, просто выберите самую легкую из возможных страниц и вставьте на нее свой скрипт. Возможно, подойдет какая-то страница "не найдена").
(4) Скрипт на скрытой странице iframe нужно только (а) установить document.domain = "example.com";
и (б) уведомить родительское окно, когда это будет сделано. После этого родительское окно может получить доступ к окну iframe и всем его объектам без ограничений! Итак, минимальная страница iframe выглядит примерно так:
<!doctype html>
<html>
<head>
<script>
document.domain = "example.com";
window.parent.iframeReady();
</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 origin - так что у них будет одно и то же общее происхождение для всех ваших страниц!
Самая неудобная часть этого метода заключается в том, что вам нужно дождаться загрузки iframe, прежде чем продолжить. Таким образом, вы не можете просто беспечно начать использовать localStorage в своем обработчике DOMContentLoaded, например. Также вы можете добавить некоторую обработку ошибок, чтобы определить, не загружается ли скрытый iframe правильно.
Очевидно, вы также должны убедиться, что скрытый iframe не удаляется или не перемещается в течение всего срока службы вашей страницы ... OTOH Я не знаю, каков будет результат, но очень вероятно, что произойдет что-то плохое.
И предостережение: настройку / изменение document.domain
можно заблокировать с помощью Feature-Policy
заголовка, и в этом случае этот метод нельзя будет использовать, как описано.
Однако существует значительно более сложное обобщение этого метода, которое не может быть заблокировано Feature-Policy
, и которое также позволяет совершенно несвязанным доменам обмениваться данными, коммуникациями и совместно используемыми рабочими (т.е. не только поддоменами из общего супердомена). @Mayank Jain уже описал это в своем ответе, а именно:
Общая идея заключается в том, что, как и выше, вы создаете скрытый iframe, чтобы обеспечить правильное происхождение для доступа; но вместо того, чтобы напрямую захватывать свойства окна iframe, вы используете сценарий внутри iframe для выполнения всей работы, и вы общаетесь между iframe и вашим главным окном, используя только postMessage()
иaddEventListener("message",...)
.
Это работает, потому что postMessage()
может использоваться даже между окнами разного происхождения. Но это также значительно сложнее, потому что вам нужно передать все через какую-то инфраструктуру обмена сообщениями, которую вы создаете между iframe и главным окном, а не просто использовать API localStorage, IndexedDB и т. Д. Непосредственно в коде вашего главного окна.