Oto jak:
W przypadku udostępniania między subdomenami danej superdomeny (np. Example.com), istnieje technika, której możesz użyć w takiej sytuacji. Może być stosowany do localStorage
, IndexedDB
, SharedWorker
, BroadcastChannel
, itp, z których wszystkie oferują wspólną funkcjonalność między stronami tego samego pochodzenia, ale z jakiegoś powodu nie przestrzegają żadnych modyfikacji document.domain
, które pozwoli im korzystać z naddomenę ich pochodzenia bezpośrednio.
(1) Wybierz jedną „główną” domenę, do której mają należeć dane: tj. Https://example.com lub https://www.example.com będzie przechowywać dane localStorage. Załóżmy, że wybierasz https://example.com .
(2) Normalnie używaj localStorage dla stron wybranej domeny.
(3) Na wszystkich stronach https://www.example.com ( inna domena) użyj javascript do ustawienia document.domain = "example.com";
. Następnie utwórz także ukrytą <iframe>
i przejdź do jakiejś strony w wybranej domenie https://example.com ( nie ma znaczenia, która strona , o ile możesz wstawić tam bardzo mały fragment kodu JavaScript. Jeśli „ ponownego tworzenia witryny, po prostu utwórz pustą stronę specjalnie w tym celu. Jeśli piszesz rozszerzenie lub skrypt użytkownika w stylu Greasemonkey, a więc nie masz żadnej kontroli nad stronami w witrynie example.comserwer, po prostu wybierz najlżejszą stronę, jaką możesz znaleźć i wstaw do niej swój skrypt. Jakaś strona „nie znaleziono” prawdopodobnie byłaby w porządku).
(4) Skrypt na ukrytej stronie iframe musi tylko (a) ustawić document.domain = "example.com";
i (b) powiadomić okno nadrzędne, kiedy to się stanie. Następnie okno nadrzędne może uzyskać dostęp do okna iframe i wszystkich jego obiektów bez ograniczeń! Zatem minimalna strona iframe wygląda mniej więcej tak:
<!doctype html>
<html>
<head>
<script>
document.domain = "example.com";
window.parent.iframeReady();
</script>
</head>
<body></body>
</html>
Pisząc skrypt użytkownika, możesz nie chcieć dodawać funkcji dostępnych z zewnątrz, takich jak iframeReady()
twój unsafeWindow
, więc zamiast tego lepszym sposobem powiadomienia skryptu użytkownika okna głównego może być użycie zdarzenia niestandardowego:
window.parent.dispatchEvent(new CustomEvent("iframeReady"));
Które można wykryć, dodając detektor dla niestandardowego zdarzenia „iframeReady” do okna strony głównej.
(UWAGA: musisz ustawić document.domain = "example.com", nawet jeśli domena iframe to już example.com : przypisanie wartości do document.domain niejawnie ustawia port pochodzenia na null, a oba porty muszą pasować do elementu iframe i jego rodzica są uważane za tego samego pochodzenia. Zobacz uwagę tutaj: https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy#Changing_origin )
(5) Po ukryte iframe poinformował swojego okna nadrzędnego, że jest gotowy, skrypt w oknie nadrzędnym może po prostu użyć iframe.contentWindow.localStorage
, iframe.contentWindow.indexedDB
, iframe.contentWindow.BroadcastChannel
, iframe.contentWindow.SharedWorker
zamiast window.localStorage
, window.indexedDB
itd ... i wszystkie te obiekty zostaną scoped na wybrany https: // example.com origin - dzięki czemu będą miały to samo wspólne źródło dla wszystkich Twoich stron!
Najbardziej niewygodną częścią tej techniki jest to, że przed kontynuowaniem musisz poczekać na załadowanie elementu iframe. Nie możesz więc po prostu beztrosko zacząć używać localStorage na przykład w module obsługi DOMContentLoaded. Możesz także dodać obsługę błędów, aby wykryć, czy ukryta ramka iframe nie ładuje się poprawnie.
Oczywiście powinieneś również upewnić się, że ukryta ramka iframe nie jest usuwana ani nawigowana przez cały czas istnienia Twojej strony ... OTOH Nie wiem, jaki byłby tego skutek, ale bardzo prawdopodobne, że wydarzy się coś złego.
I zastrzeżenie: ustawianie / zmienianie document.domain
można zablokować za pomocą Feature-Policy
nagłówka, w takim przypadku ta technika nie będzie użyteczna zgodnie z opisem.
Istnieje jednak znacznie bardziej skomplikowane uogólnienie tej techniki, którego nie można zablokować Feature-Policy
, a także umożliwia całkowicie niepowiązanym domenom udostępnianie danych, komunikacji i współdzielonych pracowników (tj. Nie tylko subdomen ze wspólnej superdomeny). @Mayank Jain opisał to już w swojej odpowiedzi, a mianowicie:
Ogólna idea jest taka, że tak jak powyżej, tworzysz ukrytą ramkę iframe, aby zapewnić prawidłowe źródło dostępu; ale zamiast po prostu pobierać bezpośrednio właściwości okna iframe, używasz skryptu wewnątrz elementu iframe, aby wykonać całą pracę, i komunikujesz się między elementem iframe a oknem głównym tylko za pomocą postMessage()
i addEventListener("message",...)
.
To działa, ponieważ postMessage()
można go używać nawet między oknami różnych źródeł. Ale jest to również znacznie bardziej skomplikowane, ponieważ musisz przepuścić wszystko przez jakąś infrastrukturę komunikacyjną, którą tworzysz między ramką iframe a oknem głównym, zamiast po prostu używać interfejsów API localStorage, IndexedDB itp. Bezpośrednio w kodzie głównego okna.