Cerrar la sesión de un usuario de un sitio web cuando apagan su computadora

11

Esta es extraña. Tenemos un sitio web de Laravel, y en dicho sitio tenemos un temporizador por usuario, donde obtienen 15 minutos de inactividad antes de ser arrancados.

Hacemos esto a través de un temporizador que se encuentra en la página en un componente de reacción, funciona como queremos, pero ahora tenemos un nuevo problema: si un usuario inicia sesión y cierra la tapa de su computadora portátil, el sitio web debería arrancarlo . Los bancos hacen esto, las escuelas y las universidades hacen esto, los sitios gubernamentales también lo hacen. Entonces es posible, pero no estoy seguro de cómo.

Usamos sockets web, usando la biblioteca laravel-websockets y Echo. Lo que me gustaría ver es:

  • Una vez que cierre su computadora portátil, inicie la pantalla de inicio de sesión. Entonces, la próxima vez que abra la computadora portátil e inicie sesión, y vea el navegador en el que se encuentra en la pantalla de inicio de sesión. No tiene que suceder tan rápido, pero necesitamos una forma de enviar algo al front-end básicamente diciéndoles que actualicen la página, una vez que finaliza la sesión, establecemos la duración de la sesión en un intervalo de 15 minutos.

Algunas personas han sugerido en otras preguntas similares:

  • para crear un controlador de socket web personalizado
  • Para comparar la cookie de sesión (en el navegador) con la cookie del usuario en el back-end.
  • Para tener un temporizador funcionando en la parte frontal (lo hacemos, simplemente se detiene cuando cierra la tapa del portátil)

El más popular parece estar usando enchufes web, escuchando al usuario desconectarlos y luego iniciarlos, lo cual está bien, pero ¿cómo se envía una solicitud a un navegador que está suspendido para luego iniciarlos?

He encontrado requestIdleCallback () Pero, de nuevo, no creo que esto sea lo que quiero si ya tengo un temporizador de latidos en el sitio. Tampoco funciona en todos los navegadores.

Estoy muy perdido aquí sobre cómo lograr esto, el ejemplo que puedo dar es:

Inicie sesión en su banco, apague su computadora, espere 15-20 minutos, despierte la computadora, inicie sesión y vea que su banco ahora lo tiene en la pantalla de inicio de sesión. Eso es lo que quiero . Pero no sé cómo lograr eso.

No puede enviar eventos a un navegador "inactivo" desde el back-end, y si bien esto tendría que ser una solución de back-end, ¿cómo puede actualizar el front-end para que estén en la pantalla de cierre de sesión cuando vuelvan a encender la computadora portátil? o computadora?

TheWebs
fuente
77
Simplemente configure la fecha de caducidad de Cooke de la sesión en "ahora + 15 minutos" ... aunque todavía puede ver la última pantalla, no puede actuar en esa sesión si la cookie ha caducado. Y la "pantalla de inicio de sesión": estoy bastante seguro de que hay una configuración del sistema operativo que cerrará automáticamente la sesión de una computadora después de un cierto tiempo de inactividad ...
Lars Stegelitz
1
Puede iniciar un temporizador de Javascript cada vez que se carga un sitio, con el mismo tiempo de espera que su cookie de sesión ... si el temporizador "suena", el usuario estuvo inactivo durante tanto tiempo. Luego lo desconectaría (llamada AJAX) y redirigiría el navegador a la pantalla de inicio de sesión o la pantalla "perdón, lo desconectamos debido a la inactividad";)
Lars Stegelitz
1
Bien, ya lo hice. Estoy agregando la respuesta ahora mismo.
Dato DT
1
Entonces, ¿fue útil?
Dato DT
1
@DatoDT No, uno uso laravel, dos ya uso los sockets laravel como se expresa en mi publicación de apertura, dos su código es muy desordenado, no OOP, no probado y nunca lo usaría. Básicamente estoy buscando una solución laravel.
TheWebs

Respuestas:

0

ACTUALIZAR

Con respecto a la solicitud de WebSocket, supongo que está utilizando Laravel WebSockets con pusher. Pusher.io no es compatible con el tiempo de espera , puede leer este artículo de soporte "¿Planea agregar una función de tiempo de espera de conexión a la biblioteca del cliente de canales pusher-js?" . Puede probarlo si habilita el modo de depuración de Laravel (APP_DEBUG=true dentro .env ) y comienza laravel-websocketsdesde la terminal ( php artisan websockets:serve) para que pueda ver los eventos de registro de salida. Si intenta cerrar la tapa de la computadora portátil o configurar la computadora en modo de hibernación ( suspensión ), no verá ningún mensaje relacionado con este evento. No puedes hacerlo con el pusherprotocolo. Existe el evento de presenciamember_removed , pero eso solo se activa cuando cierra la pestaña o cierra la sesión. Por supuesto, puede activar su evento personalizado de cliente en el canal de presencia, pero para hacerlo también necesita una configuración de temporizador en el lado del cliente y tendrá que crear un proveedor de servicios para el laravel-websocketsservidor como este problema de github "Existe una forma de implementar webhooks? " .

Algunas personas han sugerido en otras preguntas similares:

...

  • Para tener un temporizador funcionando en la parte frontal ( lo hacemos, simplemente se detiene cuando cierra la tapa del portátil )

Eso sucede porque los temporizadores de los clientes detienen la ejecución en hibernación, por lo tanto, continúan desde donde estaban antes. Pero si tu usa una variable de fecha para ahorrar tiempo , esa variable no se actualizará cuando la computadora entre en hibernación, por lo tanto , sabrá cuándo sale del modo de suspensión al verificar esa variable de fecha que, en comparación con la hora actual, tendrá un significado significativo diferencia y será mayor que el intervalo del temporizador.

Implementación de lógica de tiempo en cliente

También puede ver esta implementación en este Q / A relacionado : ¿Puede cualquier navegador de escritorio detectar cuándo la computadora se reanuda del sueño?

Puede configurar un temporizador en el cliente para que se ejecute cada minuto. No confiaremos en el intervalo del temporizador , sino que ese temporizador verificará una variable de fecha de alcance externo si el intervalo de tiempo desde el último temporizador es mayor que 15minutos; si es así, eso significa que el navegador / JS detuvo la ejecución por alguna razón , posiblemente hibernación del dispositivo ( suspensión ) y luego redirige al usuario a la ruta de cierre de sesión.

Ejemplo de código de cliente 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);

Puede consultar este sencillo ejemplo, pero usando el 1segundo temporizador con 15segundos cierre sesión aquí . Lo mejor es probarlo en una computadora portátil con el cierre de la tapa y luego abrirlo nuevamente después de 15 segundos por minuto de dos, porque si tiene muchos programas en ejecución, la computadora tarda un tiempo en guardar el estado de la memoria para completar el modo de hibernación y detener la ejecución.

Ejemplo de trabajadores web

Incluso puedes usar la API de Web Workers para configurar un trabajador web para que sea mucho más seguro:

Código de página JS:

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.jsCódigo de trabajador web :

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

También puede consultar el ejemplo de Web Worker con el mismo 15temporizador de segundos aquí .

Christos Lytras
fuente
Esto lo desconecta después de 15 segundos, sin importar qué, no estoy seguro de que esto sea lo que está buscando.
Islam Elshobokshy
@IslamElshobokshy tienes razón, gracias por la captura. Olvidé actualizar la clientSessionvariable. Puede verificar mi respuesta nuevamente, incluso he agregado un ejemplo de Web Worker.
Christos Lytras
Actualice los ejemplos de muestra que aún no funcionan. El primero te desconecta después de 15 segundos sin importar qué. El segundo nunca te desconecta.
Islam Elshobokshy
@IslamElshobokshy Lo he actualizado, por supuesto. Antes tenía un problema, y ​​ahora funciona como se esperaba. Actualice la página si no lo ha hecho o quizás agregue un parámetro similar ?v=1al final.
Christos Lytras
1
Gran solución + 3
AmerllicA
0

Primero, veamos por qué los sitios web de Banca cierran sesión después de 15 minutos sin actividad. Es un requisito de PCI para la seguridad.

Requisito PCI-DSS 8.1.8 :

8.1.8 Si una sesión ha estado inactiva durante más de 15 minutos, solicite al usuario que se vuelva a autenticar para reactivar el terminal o la sesión.

Para lograr esto, la solución es en realidad mucho más primitiva de lo que imaginas . No requiere el uso de websockets ni saber nada sobre el estado de la máquina del cliente (inactivo o despierto o de otro modo). Todo lo que se requiere es conocer el tiempo entre la solicitud actual que usa esa sesión y la última solicitud que usa la misma sesión y asegurarse de que no tengan más de 15 minutos de diferencia. Si lo son, el usuario debe volver a autenticarse. Si no lo están, puede continuar con la solicitud.

El mensaje "sesión agotada"

Probablemente se esté preguntando (si es así de simple) cómo aparece el mensaje de tiempo de espera de la sesión cuando apaga la computadora y la reactiva. Esta parte es engañosamente simple.

Cuando la computadora se pone en suspensión, el navegador desconecta todas las conexiones TCP / IP, lo que a su vez apaga el bucle de eventos en el motor de JavaScript. Entonces los temporizadores no funcionan. Pero cuando el navegador se despierta nuevamente, intenta actualizar algunas cosas, incluida la página en sí. Por lo tanto, cuando se actualiza la página, la solicitud vuelve al servidor que invoca al servidor para requerir que el usuario vuelva a autenticarse.

Sin embargo, esto no tendrá en cuenta el modal del mensaje de JavaScript (si eso es a lo que se refiere) que hacen algunos sitios web bancarios. Además, no todos los navegadores realizan una actualización completa de la página en todos los escenarios. Entonces se puede tomar otro enfoque. En lugar de tener un temporizador en el navegador que agota el tiempo de espera después de 15 minutos, simplemente puede almacenar el tiempo de carga de la página en javascript como una marca de tiempo y tener un intervalo de tiempo de 1 segundo que compara esa marca de tiempo con la marca de tiempo actual de la computadora. Si tienen más de 15 minutos de diferencia, la sesión debería finalizar.

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


};

Incluso si la computadora se duerme y el temporizador se detiene, la sesión eventualmente terminará en el lado del servidor ( consulte la sección a continuación para obtener más detalles ) y cuando la computadora se despierte nuevamente, el temporizador con un intervalo de 1 segundo finalmente se iniciará nuevamente, invocando el mensaje (como si el usuario expiró mientras la computadora estaba dormida). El tiempo perdido entre el momento en que la computadora se fue a dormir y el tiempo en que la computadora se despierta no importará, ya que la marca de tiempo permanecerá en la memoria. La desconexión entre el cliente y el servidor no es importante porque no necesitan comunicar esta información para que la sesión finalice correctamente en el lado del servidor. El servidor puede hacer su propia recolección de basura y terminar la sesión sin comunicación del cliente (es decir, de forma asincrónica ).

Lo creas o no, a los bancos no les importa la actividad dentro del cliente. Solo les importa la actividad de solicitud al servidor. Entonces, si se pregunta cómo mantienen viva la sesión durante más de 15 minutos cuando el usuario está en la misma página durante ese tiempo, simplemente envían una solicitud AJAX en segundo plano para actualizar la sesión después de preguntarle al usuario si todavía quiero continuar.

Esto se puede hacer en la misma onloaddevolución de llamada de evento que usamos anteriormente de la siguiente manera:

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


};

Manejo de terminación de sesión en el lado del servidor

Para manejar la finalización de la sesión en el lado del servidor, hay varios enfoques. Dependiendo de cuál uses, necesitarás diferentes tácticas. Uno está utilizando el controlador de sesión predeterminado de PHP y configurando session.max_lifetimeque caduque después de 15 minutos (esto elimina los datos de la sesión por completo en el lado del servidor, lo que invalida la cookie del cliente).

Si deja que lo haga el mecanismo del controlador de sesión predeterminado, puede encontrarse con problemas dependiendo de qué controlador se use (archivos, memcached, redis, personalizado, etc.).

Con los archivos (controlador predeterminado), la recolección de elementos no utilizados se realiza de una de dos maneras:

  • La mayoría de los sistemas basados ​​en Debian hacen su propio GC a través de un trabajo cron (que funciona muy bien para su escenario)
  • Otras distribuciones permiten que el mecanismo GC predeterminado de PHP lo maneje, lo que se basa en un resultado probabilístico de cada solicitud entrante a PHP que verifica el archivo mtime en los archivos de sesión y elimina los anteriores session.max_lifetime. El problema con este enfoque es que en los sitios de poco tráfico, una sesión podría permanecer allí en el servidor durante mucho tiempo hasta que lleguen suficientes solicitudes (dependiendo de la session.gc_probabilitypuntuación) para invocar al GC para limpiar los archivos de la sesión.

Con los controladores basados ​​en memcached y redis no tiene este problema. Manejarán la purga de la memoria automáticamente. Las sesiones aún pueden permanecer en la memoria física por un tiempo más allá de su vida útil, pero el demonio no podrá acceder a ellas. Si le preocupa este bit por seguridad, puede cifrar sus sesiones en reposo o encontrar un almacén de clave / valor que tenga un mecanismo GC de purga de memoria más estricto.

Con un controlador de sesión personalizado, tendrá que crear su propio mecanismo de GC. A través de esto SessionHandlerInterface, implementaría un gcmétodo que le proporcionaría el intervalo de vida útil máximo de la sesión y sería responsable de verificar si la sesión ha pasado su vida útil en función de este intervalo y de hacer su recolección de basura desde allí.

También puede configurar un punto final separado que verifique el TTL de la sesión (a través de una solicitud AJAX asíncrona en el lado del cliente) y envíe una respuesta si la sesión ha expirado (lo que obliga al javascript a volver a autenticar al usuario).

Jerife
fuente
0

Entonces Idea está detrás de setInterval y Sockets, setInterval es compatible con la mayoría de los navegadores y javascript WbsocketApi es compatible con casi todos los navegadores.

Breve descripción general: setInterval (): este comportamiento de la función se produce cuando la computadora está en modo de suspensión / suspensión / hibernación, está en pausa y cuando está en modo de despertar se reanuda.

El siguiente código hace lo siguiente, al principio (quizás al mismo tiempo, pero) comienza php server_socket escuchando las conexiones,

que la API websocket de javascript envía la marca de tiempo actual en milisegundos de marca de tiempo de Unix en cada 2 segundos que puede tener 1 segundo, depende de usted.

después de que el socket del servidor php está obteniendo este tiempo y comprueba si tiene algo como el tiempo anterior para comparar, cuando el código se instancia por primera vez, php no tiene nada como el tiempo anterior para compararlo con el tiempo enviado desde javascript websocket, entonces php no hace más que guardar este tiempo en la sesión llamada 'prev_time' y espera a que se reciban datos de otro momento desde el socket de JavaScript, por lo que aquí comienza el segundo ciclo. cuando el servidor php toma nuevos datos de tiempo de javascript WebsocketApi comprueba que tiene algo como el tiempo anterior para comparar con estos datos de tiempo recién recibidos, significa que php comprueba si existe una sesión llamada 'prev_time', ya que estamos en el segundo ciclo php descubre que existe, toma su valor y sigue$diff = $new_time - $prev_time, $ diff será de 2 segundos o 2000 milisegundos porque recuerde que nuestro ciclo setInterval ocurre en cada 2 segundos y el formato de tiempo que enviamos está en milisegundos,

que php comprueba if($diff<3000)si la diferencia es menor a 3000 si es que sabe que el usuario está activo, nuevamente puede manipular estos segundos como lo desee, elijo 3000 porque es posible una latencia en la red que es casi imposible, pero sabe que siempre soy cauteloso cuando se trata de redes, así que continuemos, cuando php determina que el usuario está activo, php simplemente restablece la sesión 'prev_time' con el valor $new_timeque se recibió recientemente y solo con fines de prueba, envía un mensaje de vuelta al socket de JavaScript,

pero si $diffes más de 3000 significa que algo pausó nuestro setInterval y solo puede suceder y creo que ya sabe lo que estoy diciendo, por lo que en la elselógica de ( if($diff<3000)) puede cerrar la sesión del usuario destruyendo una sesión específica y si desea redirigir, puede enviar algo de texto al socket javacript y crear una lógica que se ejecutará window.location = "/login"dependiendo del texto, aquí está el código:

Primero es el archivo index.html solo para cargar javascript:

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

entonces es javascript, no está muy bien codificado, pero puedes descubrir LEER COMENTARIOS QUE SON IMPORTANTES:

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);
};

ahora aquí es parte del código php, no te preocupes, también hay un código completo, pero esta parte es en realidad lo que hace los trabajos mencionados anteriormente, también cumplirás otras funciones, pero son para decodificar y trabajar con sockets javascript, por lo que es correcto aquí LEER COMENTARIOS SON IMPORTANTES:

<?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);

?>

Y aquí está el código completo de 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;
}

NOTA LEER: la $new_timevariable está $jsTimeen el código

cree la carpeta y simplemente copie y pegue esto en los archivos ejecute php socket con el comando: php -f server_socket.php vaya al localhost y pruébelo abra la consola para ver mensajes que dirá "usted está activo" o "no está activo" (cuando vienes de dormir); su ejecución ocurrirá cuando el usuario salga del modo de suspensión, no cuando esté en reposo porque en ese momento todo se almacena en caché en el archivo de paginación (windows) o en el intercambio (linux)

Dato DT
fuente
cree la carpeta y simplemente copie y pegue esto en los archivos ejecute php socket con el comando: php -f server_socket.php vaya al localhost y pruébelo abra la consola para ver mensajes que dirá "usted está activo" o "no está activo" (cuando vienes de dormir); su ejecución ocurrirá cuando el usuario salga del modo de suspensión, no cuando esté en reposo porque en ese momento todo se almacena en caché en el archivo de paginación (windows) o en el intercambio (linux)
Dato DT
0

Creo que tengo una idea, has discutido mucho sobre cómo funciona el sistema de inicio / cierre de sesión bancario.

Caso 1: acceso de la página web al usuario por tiempo ilimitado si el usuario está activo

Siempre que el usuario inicie sesión, inicie un temporizador en su backend (establezca el límite de tiempo que desee), digamos 15 minutos. Ahora que significa? Significa que si el usuario no realiza ninguna actividad en la página web, lo desconectaremos.

Ahora, desde el frente, puede enviar la actividad del usuario a su backend (podría enviarse mediante un socket o un sondeo largo), lo que básicamente restablecerá el temporizador y el usuario puede usar la página web activamente durante el tiempo que desee.

Si el usuario pone su PC en suspensión, el temporizador no se reiniciará y usted puede invalidar la sesión una vez que finalice el temporizador.

Si desea invalidar la sesión del usuario tan pronto como apaguen su PC, puede establecer el límite del tiempo de validación de la sesión. Por ejemplo, cuando el usuario inicia sesión, crearemos la sesión que será válida solo por 10 segundos, y una vez que recibamos la solicitud de actividad del usuario, podemos restablecer el temporizador y proporcionar una nueva clave de sesión.

Espero que esto te ayude. Avísame si tienes alguna pregunta.

Srivastava dura
fuente
-1

Escribí un script para detectar si la máquina se fue a dormir. La idea es que cuando la máquina esté en modo de suspensión, todas las secuencias de comandos se detendrán. Por lo tanto, si hacemos un seguimiento de la hora actual dentro de un timeInterval. Cada vez que timeInterval activa la hora actual menos (-) la nueva hora debe estar lo suficientemente cerca del timeInterval. Por lo tanto, si queremos verificar si el temporizador estuvo inactivo durante el tiempo X, podemos verificar si la diferencia horaria es mayor que X.

Por ejemplo, las comprobaciones de golpes si la computadora se puso en suspensión por más de 15 segundos. Tenga en cuenta que cuando pone la computadora en reposo, le tomará otros 15 segundos adicionales para idear todos los procesadores. (Cuando se prueba en MI 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
fuente
Por favor explique por qué esto no funcionaría, si no funciona en su caso
Lasithds
-1

He implementado exactamente el mismo requisito usando AWS Cognito, con Lambda Authorizers, y Redis, no puedo compartir el código en esta etapa, pero puedo decirle todo cómo se implementa con estos componentes, los mismos conceptos se pueden usar con otros componentes no AWS.

En primer lugar, con la implementación de un cierre de sesión de inactividad, tendrá que hacerlo del lado del servidor, ya que si alguien simplemente apaga su computadora, el sitio web front-end no los cerrará. Usé el concepto de ACTIVEusuarios. Cuando los usuarios se autentican con éxito, almaceno con un TTL de 15 minutos en Redis una entrada con una clave de su username& un valor de ACTIVE(puede ser nombre de usuario + sessionid si desea permitir múltiples sesiones para un usuario dado al mismo tiempo).

En mis Autorizadores personalizados, cuando un usuario ACTIVEtiene un token válido, les concedo acceso al recurso protegido Y, lo que es más importante, hago otra puesta en Redis con el username& ACTIVE.

Cada vez que el usuario cierra sesión, lo cierro en mi solución de administración de identidad (Cognito) y lo marco como INACTIVE. Tenga en cuenta que si un usuario no llega a la API en 15 minutos, ya no tendrá una entrada ACTIVEcontra su nombre de usuario y ya no podrá acceder a la API y tendrá que iniciar sesión nuevamente, para lo cual será redirigido.

Hay muchas cosas a tener en cuenta con este enfoque, por un lado, a menudo, los Autorizadores almacenan en caché los resultados durante cierto tiempo, y si, por ejemplo, almacena en caché el resultado durante 5 minutos, entonces su usuario podría cerrar la sesión en 10 minutos como su usuario podría golpear el caché en lugar del Autorizador, lo que no actualizaría la ACTIVEentrada.

También es importante que se asegure de que todo lo que use para almacenar si un usuario determinado ACTIVEestá altamente disponible y se recuperará rápidamente en caso de falla.

El enfoque de usar un almacén de caché de esta manera es similar a cómo la invalidación de token se adapta a protocolos de autorización sin estado como OAuth2.

Hemos estado utilizando este enfoque durante unos meses, parece que funciona bien para nosotros, puede ser un requisito un poco molesto de manejar, había esperado en el mundo de AWS que hubiera una lista para usar de la solución de caja para esto, pero no había nada de qué hablar.

Snickers3192
fuente
También sobrevivimos a una prueba de lápiz (; De aquí es de donde surgió nuestro requisito inicialmente, nuestra aplicación es un producto basado en servicios financieros, y tuvimos que implementar esto como un requisito.
Snickers3192