To dziwne. Mamy stronę internetową Laravel i na tej stronie mamy licznik czasu na użytkownika, który dostaje 15 minut nieaktywności przed uruchomieniem.
Robimy to za pomocą timera, który znajduje się na stronie w komponencie reagującym, działa tak, jak tego chcemy, ale teraz mamy nowy problem: jeśli użytkownik jest zalogowany i zamknie pokrywę swojego laptopa, witryna powinna je uruchomić . Robią to banki, szkoły i uniwersytety, witryny rządowe. Jest to możliwe, po prostu nie wiem jak.
Używamy gniazd sieciowych, używając biblioteki laravel-websockets i Echo. To, co chciałbym zobaczyć, to:
- Po zamknięciu rozruchu laptopa przejdziesz do ekranu logowania. Więc następnym razem, gdy otworzysz laptopa i zalogujesz się, i zobaczysz przeglądarkę, jesteś na ekranie logowania. Nie musi tak szybko się zdarzyć, ale potrzebujemy sposobu, aby wysłać coś do interfejsu w zasadzie mówiąc im, aby odświeżyli stronę, gdy sesja zostanie zabita, ustawiamy jej żywotność na laravel na 15 minut.
Niektóre osoby zasugerowały w innych podobnych pytaniach:
- aby utworzyć niestandardowy moduł obsługi gniazd sieciowych
- Aby porównać plik cookie sesji (w przeglądarce) z plikiem cookie użytkownika na zapleczu.
- Aby zegar działał z przodu (robimy to, zatrzymuje się po zamknięciu pokrywy laptopa)
Najbardziej popularna wydaje się używać gniazd sieciowych, nasłuchując, jak użytkownik rozłącza się, a następnie uruchamia je, co jest w porządku, i wszystko, ale w jaki sposób wysyłasz żądanie do przeglądarki, która została zawieszona, aby je następnie uruchomić?
Znalazłem requestIdleCallback () Ale znowu, nie sądzę, że tego właśnie chcę, jeśli mam już licznik pulsu na stronie. Nie działa również we wszystkich przeglądarkach.
Jestem bardzo zagubiony tutaj, jak to osiągnąć, przykład, który mogę podać, to:
Zaloguj się do swojego banku, przełącz komputer w tryb uśpienia, odczekaj 15-20 minut, obudź komputer, zaloguj się i zobacz, jak bank ma cię teraz na ekranie logowania. Właśnie tego chcę . Ale nie wiem, jak to osiągnąć.
Nie można wysyłać zdarzeń do „śpiącej” przeglądarki z zaplecza i chociaż tak, musiałoby to być rozwiązanie zaplecza, jak zaktualizować wtedy interfejs, aby pojawiały się na ekranie wylogowania, gdy ponownie obudzą laptopa czy komputer?
źródło
Odpowiedzi:
AKTUALIZACJA
Jeśli chodzi o żądanie WebSocket, zakładam, że używasz Laravel WebSockets z
pusher
. Pusher.io nie obsługuje limitu czasu , możesz przeczytać ten artykuł: „Czy planujesz dodać funkcję limitu czasu połączenia do biblioteki klienta Pusher-JS ?” . Możesz to przetestować, jeśli włączysz tryb debugowania Laravela (APP_DEBUG=true
wewnątrz.env
) i zacznieszlaravel-websockets
od terminalu (php artisan websockets:serve
), aby zobaczyć wyjściowe zdarzenia dziennika. Jeśli spróbujesz zamknąć pokrywę laptopa lub przełączyć komputer w tryb hibernacji ( uśpienia ), nie zobaczysz żadnych komunikatów dotyczących tego zdarzenia. Nie możesz tego zrobić zpusher
protokołem. Jest zdarzenie Obecnośćmember_removed
, ale uruchamia się tylko po zamknięciu karty lub wylogowaniu. Oczywiście możesz wyzwolić niestandardowe zdarzenie klienta w kanale obecności, ale aby to zrobić, musisz także skonfigurować licznik czasu po stronie klienta i musisz utworzyć dostawcę usług dlalaravel-websockets
serwera, takiego jak ten problem z githubem „Istnieje sposób na wdrożyć haki internetowe? ” .Dzieje się tak, ponieważ liczniki czasu klienta zatrzymują wykonywanie po hibernacji, a więc kontynuują pracę z miejsca, w którym były wcześniej. Ale jeśli użyjesz zmiennej daty, aby zaoszczędzić czas , ta zmienna nie zostanie zaktualizowana, gdy komputer przejdzie w stan hibernacji, więc będziesz wiedział, kiedy wychodzi ze snu, sprawdzając tę zmienną daty która w porównaniu z bieżącym czasem będzie miała znaczący różnica i będzie większa niż interwał timera.
Implementacja logiki czasu w kliencie
Możesz także zobaczyć tę implementację do tego powiązanego pytania / odpowiedzi : Czy każdy przeglądarek stacjonarnych wykryć po wznowieniu pracy komputera ze stanu uśpienia?
Możesz ustawić licznik w kliencie, aby uruchamiał się co minutę. Nie będziemy polegać na interwale timera , ale zamiast tego ten zegar sprawdzi zmienną daty zakresu zewnętrznego, jeśli czas od ostatniego timera jest dłuższy niż
15
minuty; jeśli tak jest, oznacza to, że przeglądarka / JS zatrzymało wykonywanie z jakiegoś powodu , być może hibernacji urządzenia ( uśpienia ), a następnie przekierowanie użytkownika do trasy wylogowania.Przykładowy kod klienta JS:
Możesz sprawdzić ten prosty przykład, ale używając. Najlepiej przetestuj go na laptopie z zamknięciem pokrywy, a następnie otwórz go ponownie po1
drugiego timera z15
sekundowym wylogowaniem tutaj15 sekundachna dwie , ponieważ jeśli masz uruchomionych wiele programów, komputer potrzebuje czasu na zapisanie stanu pamięci, aby zakończyć tryb hibernacji i zatrzymać wykonywanie.Przykład pracowników sieci
Możesz nawet użyć interfejsu API Web Workers, aby ustawić robota WWW na znacznie bezpieczniejszy:
Kod JS strony:
logoutWorker.js
Kod pracownika sieci :Można również sprawdzić na przykładzie Web Worker z tymi samymi
15
sekund Licznik tutaj .źródło
clientSession
zmienną. Możesz ponownie sprawdzić moją odpowiedź, dodałem nawet przykład Web Workera.?v=1
na końcu.Po pierwsze, wyjaśnijmy, dlaczego witryny bankowości wylogowują Cię po 15 minutach bez aktywności. Jest to wymóg PCI dotyczący bezpieczeństwa.
Wymaganie PCI-DSS 8.1.8 :
Aby to osiągnąć, rozwiązanie jest o wiele bardziej prymitywne, niż można sobie wyobrazić wydaje. Nie wymaga użycia gniazd sieciowych ani wiedzy o stanie komputera klienta (uśpienie, przebudzenie lub inne). Wymagana jest jedynie znajomość czasu między bieżącym żądaniem wykorzystującym tę sesję a ostatnim żądaniem używającym tej samej sesji i upewnienie się, że nie są one dłuższe niż 15 minut. Jeśli są, użytkownik musi zostać ponownie uwierzytelniony. Jeśli nie są, możesz kontynuować przesyłanie wniosku.
Komunikat „Upłynął limit czasu sesji”
Prawdopodobnie zastanawiasz się (czy to takie proste), jak pojawia się komunikat o przekroczeniu limitu czasu sesji, gdy komputer jest uśpiony i budzi się z powrotem. Ta część jest zwodniczo prosta.
Po uśpieniu komputera przeglądarka faktycznie rozłącza wszystkie połączenia TCP / IP, co z kolei wyłącza pętlę zdarzeń w silniku javascript. Czasomierze nie działają. Ale gdy przeglądarka budzi się ponownie, próbuje odświeżyć niektóre rzeczy, w tym samą stronę. Tak więc po odświeżeniu strony żądanie wraca do serwera, wywołując serwer, aby wymagać ponownego uwierzytelnienia użytkownika.
Nie będzie to jednak uwzględniać modalnego komunikatu javascript (jeśli o to chodzi), które robią niektóre witryny bankowe. Również nie wszystkie przeglądarki dokonują twardego odświeżania strony we wszystkich scenariuszach. Można więc zastosować inne podejście. Zamiast mieć w przeglądarce licznik czasu, który upływa po 15 minutach, możesz po prostu zapisać czas ładowania strony w javascript jako znacznik czasu i mieć 1-sekundowy odstęp czasu, który porównuje ten znacznik czasu z bieżącym znacznikiem czasu komputera. Jeśli dzieli ich więcej niż 15 minut, sesja powinna zostać zakończona.
Nawet jeśli komputer przejdzie w tryb uśpienia i stoper się zatrzyma, sesja ostatecznie przekroczy limit czasu po stronie serwera ( szczegóły w sekcji poniżej ), a gdy komputer ponownie się obudzi, stoper z 1 sekundową przerwą w końcu ponownie się uruchomi, wywołując wiadomość (tak jakby użytkownik przekroczył limit czasu, gdy komputer spał). Czas stracony między czasem przejścia komputera w tryb uśpienia a czasem, w którym komputer się budzi, nie będzie miał znaczenia, ponieważ znacznik czasu pozostanie w pamięci. Rozłączenie między klientem a serwerem nie jest ważne, ponieważ nie muszą one przekazywać tych informacji, aby sesja została poprawnie zakończona po stronie serwera. Serwer może wykonać własne czyszczenie pamięci i zakończyć sesję bez komunikacji z klientem (tj. Asynchronicznie ).
Wierzcie lub nie Banki nie dbają o aktywność wewnątrz klienta. Dbają tylko o aktywność związaną z żądaniami do serwera. Jeśli więc zastanawiasz się, w jaki sposób utrzymują one sesję dłużej niż 15 minut, gdy użytkownik jest na tej samej stronie przez tak długi czas, po prostu wysyłają w tle żądanie AJAX w celu odświeżenia sesji po zapytaniu użytkownika, czy nadal trwa chcę kontynuować.
Można to zrobić w tym samym
onload
wywołaniu zwrotnym zdarzenia, którego użyliśmy wcześniej:Obsługa zakończenia sesji po stronie serwera
Do obsługi zakończenia sesji po stronie serwera istnieje kilka podejść. W zależności od tego, którego użyjesz, będziesz potrzebować różnych taktyk. Jednym z nich jest użycie domyślnego modułu obsługi sesji PHP i ustawienie
session.max_lifetime
wygasania po 15 minutach (usuwa to dane sesji całkowicie po stronie serwera, tym samym unieważniając ciasteczko klienta).Jeśli zezwolisz na to domyślnemu mechanizmowi obsługi sesji, możesz napotkać problemy w zależności od tego, który moduł obsługi jest używany (pliki, memcached, redis, niestandardowe itp.).
W przypadku plików (domyślny moduł obsługi) czyszczenie pamięci odbywa się na jeden z dwóch sposobów:
session.max_lifetime
. Problem z tym podejściem polega na tym, że w witrynach o niskim natężeniu ruchu sesja może potencjalnie pozostawać na serwerze przez długi czas, dopóki nie pojawi się wystarczająca liczba żądań (w zależności odsession.gc_probability
wyniku), aby wywołać GC w celu wyczyszczenia plików sesji.Z programami obsługi memcached i redis nie masz tego problemu. Poradzą sobie z czyszczeniem pamięci automatycznie. Sesje mogą nadal pozostawać w pamięci fizycznej przez pewien czas, ale demon nie będzie mógł uzyskać do nich dostępu. Jeśli martwisz się tym bitem ze względów bezpieczeństwa, możesz zaszyfrować swoje sesje w spoczynku lub znaleźć magazyn kluczy / wartości, który ma ściślejszy mechanizm czyszczenia pamięci GC.
Za pomocą niestandardowego modułu obsługi sesji będziesz musiał zbudować własny mechanizm GC. Poprzez
SessionHandlerInterface
chcesz wdrożyćgc
metodę, która ręka Ci maksymalny czas życia Sesji i byłbyś odpowiedzialny za sprawdzenie, czy sesja minęła jej żywotność na podstawie tego przedziału i zrobić swoją kolekcję śmieci stamtąd.Można również skonfigurować osobny punkt końcowy, który sprawdza sesję TTL (poprzez asynchroniczne żądanie AJAX po stronie klienta) i odsyła odpowiedź, jeśli sesja wygasła (zmuszając javascript do ponownej autoryzacji użytkownika).
źródło
Więc Idea stoi za setInterval i Sockets, setInterval jest obsługiwany w większości przeglądarek, a javascript WbsocketApi jest obsługiwany w prawie każdym przeglądarce.
Krótki przegląd: setInterval () - funkcja ta zachowuje się, gdy komputer znajduje się w trybie uśpienia / zawieszenia / hibernacji, jest wstrzymany, a po przejściu w tryb czuwania wznawia się.
Poniższy kod wykonuje następujące czynności, na początku (może w tym samym czasie, ale) uruchamia php server_socket nasłuchując połączeń,
niż javascript websocket api wysyła bieżący znacznik czasu w uniksowych znacznikach czasu w milisekundach co 2 sekundy, możesz mieć 1 sekundę, to zależy od Ciebie.
po tym czasie gniazdo serwera php dostaje ten czas i sprawdza, czy ma coś takiego jak poprzedni czas do porównania, kiedy kod jest tworzony po raz pierwszy, php nie ma nic takiego jak poprzedni czas, aby porównać go z czasem, który został wysłany z javascript websocket, więc php oszczędza tylko ten czas w sesji o nazwie „prev_time” i czeka na kolejne dane, które zostaną pobrane z gniazda javascript, więc tutaj zaczyna się drugi cykl. kiedy serwer php umieszcza nowe dane czasu z javascript WebsocketApi, sprawdza, czy ma coś takiego jak poprzedni czas, aby porównać je z nowo otrzymanymi danymi czasu, oznacza to, że php sprawdza, czy sesja o nazwie „prev_time” istnieje, ponieważ jesteśmy w drugim cyklu php odkrywa, że istnieje, chwyta swoją wartość i śledzi
$diff = $new_time - $prev_time
, $ diff wyniesie 2 sekundy lub 2000 milisekund, ponieważ pamiętaj, że nasz cykl setInterval odbywa się co 2 sekundy, a format czasu, który wysyłamy, jest milisekundowy,niż php sprawdza,
if($diff<3000)
czy różnica jest mniejsza niż 3000, jeśli wie, że użytkownik jest aktywny, ponownie możesz manipulować tymi sekundami, jak chcesz, wybieram 3000, ponieważ możliwe opóźnienie w sieci jest prawie niemożliwe, ale wiesz, że zawsze jestem ostrożny, gdy przychodzi do sieci, więc kontynuujmy, gdy php ustali, że użytkownik jest aktywny. php po prostu resetuje sesję „prev_time”, której wartość$new_time
została nowo odebrana, i tylko w celach testowych wysyła wiadomość z powrotem do gniazda javascript,ale jeśli
$diff
jest większy niż 3000, oznacza to, że coś wstrzymało nasz interwał przedziału i istnieje tylko jeden sposób, aby to się wydarzyło i myślę, że już wiesz, co mówię, więc zgodnie zelse
logiką (if($diff<3000)
) możesz wylogować użytkownika, niszcząc określoną sesję i jeśli chcesz przekierować możesz wysłać trochę tekstu do gniazda javacript i stworzyć logikę, która będzie wykonywana wwindow.location = "/login"
zależności od tekstu, oto kod:Najpierw jest to plik index.html tylko do załadowania javascript:
to jest javascript, nie jest naprawdę pięknie zakodowany, ale możesz dowiedzieć się, PRZECZYTAJ UWAGI, KTÓRE SĄ WAŻNE:
teraz tutaj jest częścią kodu php, nie martw się, jest też pełny kod, ale ta część jest właściwie tym, co robi wyżej wymienione zadania, spełnisz również inne funkcje, ale służą one do dekodowania i pracy z gniazdami javascript, więc jest to właściwe tutaj PRZECZYTAJ UWAGI, KTÓRE SĄ WAŻNE:
A oto pełny kod php:
UWAGA PRZECZYTAJ:
$new_time
zmienna znajduje się$jsTime
w kodzieutwórz folder i po prostu skopiuj i wklej to w plikach uruchom gniazdo php za pomocą polecenia: php -f server_socket.php przejdź do localhost i przetestuj go otwórz konsolę, aby zobaczyć komunikaty z komunikatem „jesteś aktywny” lub „nie jesteś aktywny” (kiedy przychodzisz ze snu); Twoje wykonanie nastąpi, gdy użytkownik przejdzie ze snu, a nie kiedy jest w trybie uśpienia, ponieważ w tym momencie wszystko jest buforowane w pliku stronicowania (Windows) lub w zamianie (Linux)
źródło
Myślę, że mam pomysł, dużo rozmawiałeś o tym, jak działa system logowania / wylogowywania z banku.
Przypadek 1: Dostęp do strony internetowej dla użytkownika przez nieograniczony czas, jeśli użytkownik jest aktywny
Za każdym razem, gdy użytkownik się loguje, uruchom licznik czasu na swoim backendie (ustaw limit czasu, jaki chcesz), powiedzmy 15 minut. Co to znaczy? Oznacza to, że jeśli użytkownik nie wykona żadnej czynności na stronie, wylogujemy go.
Teraz z przodu możesz wysłać aktywność użytkownika do swojego zaplecza (może być wysłana przy użyciu gniazda lub długiego odpytywania), co w zasadzie zresetuje licznik czasu, a użytkownik może aktywnie korzystać ze strony internetowej o dowolnej porze.
Jeśli użytkownik przejdzie w tryb uśpienia, licznik czasu nie zresetuje się, a sesję można unieważnić po zakończeniu licznika.
Jeśli chcesz unieważnić sesję użytkownika, gdy tylko uśpią one komputer, możesz ustawić limit czasu sprawdzania poprawności sesji. Na przykład, gdy użytkownik się zaloguje, utworzymy sesję, która będzie ważna tylko przez 10 sekund, a gdy otrzymamy żądanie aktywności użytkownika, możemy zresetować licznik czasu i podać nowy klucz sesji.
Mam nadzieję, że to Ci pomoże. Daj mi znać, jeśli masz jakieś pytania.
źródło
Napisałem skrypt wykrywający, czy maszyna poszła spać. Chodzi o to, że gdy maszyna jest w trybie uśpienia, wszystkie skrypty zostaną zatrzymane. Dlatego jeśli śledzimy bieżący czas w interwale czasu. Za każdym razem, gdy timeInterval wyzwala bieżący czas minus (-), nowy czas powinien być wystarczająco zbliżony do timeInterval. Dlatego jeśli chcemy sprawdzić, czy czasomierz był bezczynny przez czas X, możemy sprawdzić, czy różnica czasu jest większa niż X.
Przykładowy cios sprawdza, czy komputer został uśpiony na dłużej niż 15 sekund. Pamiętaj, że kiedy uśpisz komputer, potrzeba około 15 dodatkowych, aby wymyślić wszystkie procesory. (Podczas testowania na MOIM PC).
źródło
Zaimplementowałem dokładnie to samo wymaganie za pomocą AWS Cognito, z Lambda Authorizers, i Redis, nie mogę udostępniać kodu na tym etapie, ale mogę ci powiedzieć wszystko, jak jest implementowany z tymi komponentami, te same koncepcje mogą być używane z innymi elementy inne niż AWS.
Po pierwsze, po wdrożeniu wylogowania z nieaktywności, musisz to zrobić po stronie serwera, tak jakby ktoś po prostu wyłączył komputer, strona frontonu nie wylogowałaby go. Użyłem koncepcji
ACTIVE
użytkowników. Gdy użytkownicy pomyślnie się uwierzytelnią, przechowuję z TTL 15 minut w Redis wpis z kluczem ichusername
i wartościąACTIVE
(może to być nazwa użytkownika + sessionid, jeśli chcesz zezwolić na wiele sesji dla danego użytkownika w tym samym czasie).W moich niestandardowych Autoryzatorach, gdy użytkownik jest
ACTIVE
posiadaczem ważnego tokena, udzielam mu dostępu do chronionego zasobu ORAZ, co najważniejsze, robię kolejne wprowadzenie w Redis za pomocąusername
&ACTIVE
.Za każdym razem, gdy użytkownik się wylogowuje, wylogowuję je w moim rozwiązaniu do zarządzania tożsamością (Cognito) i oznaczam je jako
INACTIVE
. Pamiętaj, że jeśli użytkownik nie trafi do interfejsu API w ciągu 15 minut, nie będzie już miał wpisu wACTIVE
stosunku do swojej nazwy użytkownika i nie będzie mógł uzyskać dostępu do interfejsu API i będzie musiał się ponownie zalogować, do czego zostanie przekierowany.Przy takim podejściu należy rozważyć wiele rzeczy, z jednej strony często Autoryzatorzy przechowują wyniki w pamięci podręcznej przez pewien czas, a jeśli powiesz, że przechowujesz wyniki przez 5 minut jako przykład, użytkownik może zostać wylogowany za 10 minut jako użytkownik może trafić do pamięci podręcznej zamiast do Autoryzatora, co nie odświeży
ACTIVE
wpisu.Ważne jest również, aby upewnić się, że wszystko, czego używasz do przechowywania, jeśli dany użytkownik
ACTIVE
jest wysoce dostępny i szybko odzyska w razie awarii.Podejście polegające na użyciu w ten sposób magazynu pamięci podręcznej jest podobne do sposobu, w jaki unieważnianie tokenu jest instalowane w bezstanowych protokołach autoryzacji, takich jak OAuth2.
Stosujemy to podejście od kilku miesięcy, wydaje się, że działa dobrze, może być trochę irytujące wymaganie do spełnienia, spodziewałem się w świecie AWS, że będzie gotowy do użycia rozwiązania pudełkowego do tego, ale nie było o czym mówić.
źródło