Wyloguj użytkownika ze strony internetowej, gdy uśpi komputer

11

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:

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?

TheWebs
źródło
7
Po prostu ustaw datę wygaśnięcia sesji gotowania na „teraz + 15 minut” ... chociaż nadal możesz zobaczyć ostatni ekran, nie możesz działać w tej sesji, jeśli plik cookie wygasł. I ekran „boot to login” - jestem pewien, że istnieje ustawienie systemu operacyjnego, które automatycznie wyloguje komputer po pewnym czasie bezczynności.
Lars Stegelitz,
1
Możesz zainicjować licznik Javascript za każdym razem, gdy strona jest ładowana, z takim samym limitem czasu jak plik cookie sesji ... jeśli licznik czasu „dzwoni”, użytkownik był nieaktywny przez tak długi czas. Wylogowałbyś go (połączenie AJAX) i przekierowałeś przeglądarkę na ekran logowania lub ekran „przepraszamy, wylogowaliśmy cię z powodu braku aktywności”;)
Lars Stegelitz
1
Dobra, zrobiłem to. Teraz dodam odpowiedź.
Dato DT
1
więc to było przydatne?
Dato DT
1
@DatoDT Nie, jeden używam laravel, dwa już używam gniazd laravel, jak wyrażono w moim poście otwierającym, dwa twój kod jest bardzo niechlujny, nie OOP, nie testowany i nigdy go nie użyję. Zasadniczo szukam rozwiązania laravel.
TheWebs

Odpowiedzi:

0

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 zaczniesz laravel-websocketsod 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ć z pusherprotokoł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 dla laravel-websocketsserwera, takiego jak ten problem z githubem „Istnieje sposób na wdrożyć haki internetowe? ” .

Niektóre osoby zasugerowały w innych podobnych pytaniach:

...

  • Aby zegar działał z przodu ( robimy to, zatrzymuje się po zamknięciu pokrywy laptopa )

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ż 15minuty; 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:

// Set a variable to check previous time
let clientSession = new Date;

// Setup the client session checking timer
let clientSessionTimer = setInterval(() => {
  const now = new Date;
  // Get how many seconds have passed since last check
  const secondsSpan = (now - clientSession) / 1000;

  // If the 1 minute timer has exceeded 15 minutes trigger logout and clear timer
  if (secondsSpan > (60 * 15)) {
    // For some reason JS halted execution, so we'll proceed with logging out
    clearInterval(clientSessionTimer);
    window.location.href = '/logout/session'
  } else {
    // The timer runs as it should, update the clientSession time
    clientSession = now;
  }

}, 1000 * 60);

Możesz sprawdzić ten prosty przykład, ale używając 1drugiego timera z 15sekundowym wylogowaniem tutaj . Najlepiej przetestuj go na laptopie z zamknięciem pokrywy, a następnie otwórz go ponownie po 15 sekundach na 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:

const logoutWorker = new Worker('logoutWorker.js');
logoutWorker.onmessage = function (ev) {

  if (ev && ev.data === 'wakeup') {
    logoutWorker.terminate();
    // window.location.href = '/logout/session'
  } else {
    // The timer runs as it should, nothing to do
  }
}

logoutWorker.jsKod pracownika sieci :

let clientSession = new Date();

let clientSessionTimer = setInterval(() => {
  const now = new Date;
  const secondsSpan = (now - clientSession) / 1000;

  if (secondsSpan > 15) {
    postMessage('wakeup'); // Send a message wakeup to the worker page
    clearInterval(clientSessionTimer); // Clear the timer
  } else {
    clientSession = now; // Update the clientSession timer variable
    postMessage('update'); // And post a message to the page ONLY IF needed
  }
}, 1000);

Można również sprawdzić na przykładzie Web Worker z tymi samymi 15sekund Licznik tutaj .

Christos Lytras
źródło
To wylogowuje cię po 15 sekundach bez względu na wszystko, nie jestem pewien, czy tego właśnie szuka.
Islam Elshobokshy
@IslamElshobokshy masz rację, dzięki za złapanie. Zapomniałem zaktualizować clientSessionzmienną. Możesz ponownie sprawdzić moją odpowiedź, dodałem nawet przykład Web Workera.
Christos Lytras
Zaktualizuj przykładowe przykłady, które nadal nie działają. Pierwszy wylogowuje cię po 15 sekundach bez względu na wszystko. Drugi nigdy cię nie wylogowuje.
Islam Elshobokshy
@IslamElshobokshy Oczywiście zaktualizowałem to. Wcześniej miał problem, a teraz działa zgodnie z oczekiwaniami. Odśwież stronę, jeśli nie masz, a może dodaj parametr ?v=1na końcu.
Christos Lytras
1
Świetne rozwiązanie + 3
AmerllicA
0

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 :

8.1.8 Jeśli sesja była bezczynna przez ponad 15 minut, należy ponownie uwierzytelnić użytkownika, aby ponownie aktywować terminal lub sesję.

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.

window.onload = function() {

    sessionStart = Date.now();
    timer = setInterval(function() {
        if (Date.now() - sessionStart > 15 * 60 * 1000) {
            clearTimeout(timer);
            alert("Session Timed out!");
            window.location = "http://www.example.com/login";
        }
    }, 1000);


};

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 onloadwywołaniu zwrotnym zdarzenia, którego użyliśmy wcześniej:

window.onload = function() {

    sessionStart = Date.now();
    timer = setInterval(function() {
        if (Date.now() - sessionStart > 10 * 60 * 1000) {
           if (confirm("Your session is about to timeout. Do you wish to continue?")) {
                // send ajax request to refresh session TTL here
                // reset the timer
                sessionStart = Date.now();
            }
        } else if (Date.now() - sessionStart > 15 * 60 * 1000) {
            clearTimeout(timer);
            alert("Session Timed out!");
            window.location = "http://www.example.com/login";
        }
    }, 1000);


};

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 ustawieniesession.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:

  • Większość systemów opartych na Debianie wykonuje własne GC poprzez zadanie crona (które świetnie sprawdza się w twoim scenariuszu)
  • Inne dystrybucje pozwalają domyślnemu mechanizmowi GC na obsługę PHP, który jest oparty na prawdopodobieństwie wyniku każdego przychodzącego żądania do PHP, które sprawdza pliki mtime w plikach sesji i usuwa te z przeszłości 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 od session.gc_probabilitywyniku), 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 SessionHandlerInterfacechcesz wdrożyć gcmetodę, 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).

szeryf
źródło
0

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_timezostała nowo odebrana, i tylko w celach testowych wysyła wiadomość z powrotem do gniazda javascript,

ale jeśli $diffjest 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 z elselogiką ( 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 w window.location = "/login"zależności od tekstu, oto kod:

Najpierw jest to plik index.html tylko do załadowania javascript:

<html>
    <body>
        <div id="printer"></div>
        <script src="javascript_client_socket.js"></script>
    </body>
</html>

to jest javascript, nie jest naprawdę pięknie zakodowany, ale możesz dowiedzieć się, PRZECZYTAJ UWAGI, KTÓRE SĄ WAŻNE:

var socket = new WebSocket('ws://localhost:34237'); // connecting to socket
    // Open the socket
socket.onopen = function(event) { // detecting when connection is established
        setInterval(function(){ //seting interval for 2 seconds
            var date = new Date(); //grabing current date
            var nowtime = Date.parse(date); // parisng it in miliseconds
            var msg = 'I am the client.'; //jsut testing message


            // Send an initial message
            socket.send(nowtime); //sending the time to php socket
    },2000);

};


// Listen for messages
socket.onmessage = function(event) { //print text which will be sent by php socket 
    console.log('php: ' + event.data);
};

// Listen for socket closes
socket.onclose = function(event) {
    console.log('Client notified socket has closed', event);
};

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:

<?php 
            $decoded_data = unmask($data /* $data is actual data received from javascript socket */); //grabbing data and unmasking it | unmasking is for javascript sockets don't mind this
            print("< ".$decoded_data."\n");
            $response = strrev($decoded_data);
            $jsTime = (int) $decoded_data; /* time sent by javascript in MILISECONDS IN UNIX FORMAT  */
            if (isset($_SESSION['prev_time'])) { /** check if we have stored previous time in the session */
               $prev_time = (int) $_SESSION['prev_time']; /** grabbing the previous time from session */
               $diff = $jsTime-$prev_time; /** getting the difference newly sent time and previous time by subtracting */
               print("$jsTime - $prev_time = $diff"); /** printing the difference */
               if($diff<3000){ /** checking if difference is less than 3 second if it is it means pc was not at sleep
                               *** you can manipulate and have for example 1 second = 1000ms */
                    socket_write($client,encode("You are active! your pc is awakend"));
                    $_SESSION['prev_time'] = $jsTime; /** saving newly sent time as previous time for future testing whcih will happen in two seconds in our case*/
                }else { /** if it is more than 3 seconds it means that javascript setInterval function was paused and resumed after 3 seconds 
                            ** So it means that it was at sleep because when your PC is at sleep/suspended/hibernate mode setINterval gets pauesd */
                    socket_write($client,encode("You are not active! your pc is at sleep"));
                    $_SESSION['prev_time'] = $jsTime;
                }
            }else { /** if we have not saved the previous time in session save it  */
                $_SESSION['prev_time'] = $jsTime;
            }

            print_r($_SESSION);

?>

A oto pełny kod php:

<?php
//Code by: Nabi KAZ <www.nabi.ir>
session_abort();
// set some variables
$host = "127.0.0.1";
$port = 34237;
date_default_timezone_set("UTC");


// don't timeout!
set_time_limit(0);

// create socket
$socket = socket_create(AF_INET, SOCK_STREAM, 0)or die("Could not create socket\n");

// bind socket to port
$result = socket_bind($socket, $host, $port)or die("Could not bind to socket\n");

// start listening for connections
$result = socket_listen($socket, 20)or die("Could not set up socket listener\n");

$flag_handshake = false;
$client = null;
do {
    if (!$client) {
        // accept incoming connections
        // client another socket to handle communication
        $client = socket_accept($socket)or die("Could not accept incoming connection\n");
    }

    $bytes =  @socket_recv($client, $data, 2048, 0);
    if ($flag_handshake == false) {
        if ((int)$bytes == 0)
            continue;
        //print("Handshaking headers from client: ".$data."\n");
        if (handshake($client, $data, $socket)) {
            $flag_handshake = true;
        }
    }
    elseif($flag_handshake == true) {

        /*
        **** Main section for detectin sleep or not **
        */
        if ($data != "") {
            $decoded_data = unmask($data /* $data is actual data received from javascript socket */); //grabbing data and unmasking it | unmasking is for javascript sockets don't mind this
            print("< ".$decoded_data."\n");
            $response = strrev($decoded_data);
            $jsTime = (int) $decoded_data; /* time sent by javascript in MILISECONDS IN UNIX FORMAT  */
            if (isset($_SESSION['prev_time'])) { /** check if we have stored previous time in the session */
               $prev_time = (int) $_SESSION['prev_time']; /** grabbing the previous time from session */
               $diff = $jsTime-$prev_time; /** getting the difference newly sent time and previous time by subtracting */
               print("$jsTime - $prev_time = $diff"); /** printing the difference */
               if($diff<3000){ /** checking if difference is less than 3 second if it is it means pc was not at sleep
                               *** you can manipulate and have for example 1 second = 1000ms */
                    socket_write($client,encode("You are active! your pc is awakend"));
                    $_SESSION['prev_time'] = $jsTime; /** saving newly sent time as previous time for future testing whcih will happen in two seconds in our case*/
                }else { /** if it is more than 3 seconds it means that javascript setInterval function was paused and resumed after 3 seconds 
                            ** So it means that it was at sleep because when your PC is at sleep/suspended/hibernate mode setINterval gets pauesd */
                    socket_write($client,encode("You are not active! your pc is at sleep"));
                    $_SESSION['prev_time'] = $jsTime;
                }
            }else { /** if we have not saved the previous time in session save it  */
                $_SESSION['prev_time'] = $jsTime;
            }

            print_r($_SESSION);

           /*
        **** end of Main section for detectin sleep or not **
        */ 


        }
    }
} while (true);

// close sockets
socket_close($client);
socket_close($socket);
$client = null;
$flag_handshake = false;

function handshake($client, $headers, $socket) {

    if (preg_match("/Sec-WebSocket-Version: (.*)\r\n/", $headers, $match))
        $version = $match[1];
    else {
        print("The client doesn't support WebSocket");
        return false;
    }

    if ($version == 13) {
        // Extract header variables
        if (preg_match("/GET (.*) HTTP/", $headers, $match))
            $root = $match[1];
        if (preg_match("/Host: (.*)\r\n/", $headers, $match))
            $host = $match[1];
        if (preg_match("/Origin: (.*)\r\n/", $headers, $match))
            $origin = $match[1];
        if (preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $headers, $match))
            $key = $match[1];

        $acceptKey = $key.'258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
        $acceptKey = base64_encode(sha1($acceptKey, true));

        $upgrade = "HTTP/1.1 101 Switching Protocols\r\n".
            "Upgrade: websocket\r\n".
            "Connection: Upgrade\r\n".
            "Sec-WebSocket-Accept: $acceptKey".
            "\r\n\r\n";

        socket_write($client, $upgrade);
        return true;
    } else {
        print("WebSocket version 13 required (the client supports version {$version})");
        return false;
    }
}

function unmask($payload) {
    $length = ord($payload[1]) & 127;

    if ($length == 126) {
        $masks = substr($payload, 4, 4);
        $data = substr($payload, 8);
    }
    elseif($length == 127) {
        $masks = substr($payload, 10, 4);
        $data = substr($payload, 14);
    }
    else {
        $masks = substr($payload, 2, 4);
        $data = substr($payload, 6);
    }

    $text = '';
    for ($i = 0; $i < strlen($data); ++$i) {
        $text .= $data[$i] ^ $masks[$i % 4];
    }
    return $text;
}

function encode($text) {
    // 0x1 text frame (FIN + opcode)
    $b1 = 0x80 | (0x1 & 0x0f);
    $length = strlen($text);

    if ($length <= 125)
        $header = pack('CC', $b1, $length);
    elseif($length > 125 && $length < 65536)$header = pack('CCS', $b1, 126, $length);
    elseif($length >= 65536)
    $header = pack('CCN', $b1, 127, $length);

    return $header.$text;
}

UWAGA PRZECZYTAJ: $new_timezmienna znajduje się $jsTimew kodzie

utwó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)

Dato DT
źródło
utwó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 wymianie (linux)
Dato DT
0

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.

Surowy Srivastava
źródło
-1

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).

(function() {
    this.SleepTimer = function() {
        // console.log('sleep timer initiated');
        // Create global element references
        this.sleepTimer = null;
        this.maxTime = null;
        this.curDate = null;
        this.newDate = null;
        this.timer = null;
        this.timeInterval = 1000;

        this.sleepTimer = new CustomEvent("sleepTimer", {
		    "detail": {
		    	"maxTime":this.maxTime,
				"idelFor": this.newDate - this.curDate,
				"timer": this.timer
			}
		});

        // Define option defaults
        var defaults = {
            maxTime: 10000,
            timeInterval: 1000,
            autoStart: true,
            console: false,
            onStart: null,
            onIdel: null
        }
        // Create options by extending defaults with the passed in arugments
        if (arguments[0] && typeof arguments[0] === "object") {
            this.options = extendDefaults(defaults, arguments[0]);
        }
        if (this.options.timeInterval) {
            this.timeInterval = Math.max(1000, this.options.timeInterval);
            this.maxTime = Math.max(this.options.maxTime, 10000);
        } else {
        	this.options = defaults;
        }

        if(this.options.autoStart === true) this.start()
        // Utility method to extend defaults with user options
        
    }
    function extendDefaults(source, properties) {
        var property;
        for (property in properties) {
            if (properties.hasOwnProperty(property)) {
                source[property] = properties[property];
            }
        }
        return source;
    }
    SleepTimer.prototype.start = function(){
        var _ = this;
    	this.options.onStart()
        this.curDate = Date.now();

        this.timer = setInterval(function() {
            _.newDate = Date.now();
            var diff = _.newDate - _.curDate;

            // for debugging
            if(_.options.console && diff > _.timeInterval){
            	console.log('Your PC was idel for ' + diff / 1000 + 's of ' + _.maxTime /1000 + 's. TimeInterval is set to ' + _.timeInterval / 1000 + 's');
            }
            
            if (diff < _.maxTime) {
                _.curDate = _.newDate;
            } else {
            	_.options.onIdel();
                // alert('You have been idle for ' + diff / 1000 + 's');
                clearTimeout(_.timer);
            }
        }, this.timeInterval); // seconds
    }
}());

var sleepTimer = new SleepTimer({
	maxTime: 15000,
	console: true,
	onStart: function(){
		console.log('sleepTimer started.');
	},
	onIdel: function(){
		alert('Your session expired! Please login again.');
	}
});

Lasithds
źródło
Wyjaśnij, dlaczego to nie zadziała, jeśli w twoim przypadku nie
zadziała
-1

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 ACTIVEużytkowników. Gdy użytkownicy pomyślnie się uwierzytelnią, przechowuję z TTL 15 minut w Redis wpis z kluczem ich usernamei 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 ACTIVEposiadaczem 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 w ACTIVEstosunku 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 ACTIVEwpisu.

Ważne jest również, aby upewnić się, że wszystko, czego używasz do przechowywania, jeśli dany użytkownik ACTIVEjest 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ć.

Snickers3192
źródło
Przeżyliśmy także test długopisu (stąd pochodzi nasze wymaganie, nasza aplikacja jest produktem opartym na usługach finansowych i musieliśmy to wdrożyć jako wymóg.
Snickers3192