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?
fuente
Respuestas:
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 comienzalaravel-websockets
desde 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 elpusher
protocolo. 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 ellaravel-websockets
servidor como este problema de github "Existe una forma de implementar webhooks? " .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
15
minutos; 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:
Puede consultar este sencillo ejemplo, pero usando el. Lo mejor es probarlo en una computadora portátil con el cierre de la tapa y luego abrirlo nuevamente después de1
segundo temporizador con15
segundos cierre sesión aquí15 segundospor 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:
logoutWorker.js
Código de trabajador web :También puede consultar el ejemplo de Web Worker con el mismo
15
temporizador de segundos aquí .fuente
clientSession
variable. Puede verificar mi respuesta nuevamente, incluso he agregado un ejemplo de Web Worker.?v=1
al final.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 :
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.
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
onload
devolución de llamada de evento que usamos anteriormente de la siguiente manera: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_lifetime
que 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:
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 lasession.gc_probability
puntuació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 ungc
mé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).
fuente
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_time
que se recibió recientemente y solo con fines de prueba, envía un mensaje de vuelta al socket de JavaScript,pero si
$diff
es 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 laelse
ló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:
entonces es javascript, no está muy bien codificado, pero puedes descubrir LEER COMENTARIOS QUE SON IMPORTANTES:
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:
Y aquí está el código completo de php:
NOTA LEER: la
$new_time
variable está$jsTime
en el códigocree 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)
fuente
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.
fuente
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).
fuente
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
ACTIVE
usuarios. Cuando los usuarios se autentican con éxito, almaceno con un TTL de 15 minutos en Redis una entrada con una clave de suusername
& un valor deACTIVE
(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
ACTIVE
tiene un token válido, les concedo acceso al recurso protegido Y, lo que es más importante, hago otra puesta en Redis con elusername
&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 entradaACTIVE
contra 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
ACTIVE
entrada.También es importante que se asegure de que todo lo que use para almacenar si un usuario determinado
ACTIVE
está 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.
fuente