C'est bizarre. Nous avons un site Web Laravel, et sur ce site, nous avons un minuteur par utilisateur, où ils obtiennent 15 minutes d'inactivité avant d'être démarrés.
Nous le faisons via une minuterie qui se trouve sur la page dans un composant React, cela fonctionne comme nous le voulons, mais nous avons maintenant un nouveau problème: si un utilisateur est connecté et ferme le couvercle de son ordinateur portable, le site Web devrait le démarrer . Les banques le font, les écoles et les universités le font, les sites gouvernementaux le font aussi. Il est donc possible, mais je ne sais pas comment.
Nous utilisons des sockets Web, en utilisant la bibliothèque laravel-websockets et Echo. Ce que j'aimerais voir se produire, c'est:
- Une fois que vous avez fermé le démarrage de votre ordinateur portable, vous accédez à l'écran de connexion. Ainsi, la prochaine fois que vous ouvrirez l'ordinateur portable et que vous vous connecterez, vous verrez le navigateur sur l'écran de connexion. Cela ne doit pas se produire aussi rapidement, mais nous avons besoin d'un moyen d'envoyer quelque chose au front-end en leur disant essentiellement de rafraîchir la page, une fois la session interrompue, nous définissons la durée de vie de la session sur un laravel de 15 minutes.
Certaines personnes ont suggéré dans d'autres questions similaires:
- créer un gestionnaire de socket Web personnalisé
- Pour comparer le cookie de session (dans le navigateur) avec le cookie utilisateur à l'arrière.
- Pour avoir une minuterie en cours d'exécution à l'avant (nous le faisons, il s'arrête juste lorsque vous fermez le couvercle de l'ordinateur portable)
Le plus populaire semble utiliser des sockets Web, écouter l'utilisateur se déconnecter puis les démarrer, ce qui est bien, mais comment envoyer une demande à un navigateur suspendu pour ensuite les démarrer?
J'ai trouvé requestIdleCallback () Mais encore une fois, je ne pense pas que c'est ce que je veux si j'ai déjà un chronomètre sur le site. Cela ne fonctionne pas non plus dans tous les navigateurs.
Je suis très perdu ici sur la façon d'y parvenir, l'exemple que je peux donner est:
Connectez-vous à votre banque, mettez votre ordinateur en veille, attendez 15 à 20 minutes, réveillez l'ordinateur, connectez-vous et voyez que votre banque vous a maintenant sur l'écran de connexion. Voilà ce que je veux . Mais je ne sais pas comment y parvenir.
Vous ne pouvez pas envoyer d'événements à un navigateur "en veille" à partir du back-end, et même si cela doit être une solution back-end, comment mettez-vous à jour le front-end, de sorte qu'ils soient sur l'écran de déconnexion lorsqu'ils réveillent l'ordinateur portable ou ordinateur?
la source
Réponses:
MISE À JOUR
Concernant la demande WebSocket, je suppose que vous utilisez Laravel WebSockets avec
pusher
. Pusher.io ne prend pas en charge le délai d'expiration , vous pouvez lire cet article de support " Envisagez -vous d'ajouter une fonctionnalité de délai d'expiration de connexion à la bibliothèque client des canaux pusher-js?" . Vous pouvez le tester si vous activez le mode de débogage de Laravel (à l'APP_DEBUG=true
intérieur.env
) et commencez àlaravel-websockets
partir du terminal (php artisan websockets:serve
) afin que vous puissiez voir les événements du journal de sortie. Si vous essayez de fermer le couvercle de l'ordinateur portable ou de mettre l'ordinateur en mode hibernation ( veille ), vous ne verrez aucun message concernant cet événement. Vous ne pouvez pas le faire avec lepusher
protocole. Il y a l' événement Présencemember_removed
, mais cela ne se déclenche que lorsque vous fermez l'onglet ou que vous vous déconnectez. Bien sûr, vous pouvez déclencher votre événement personnalisé client sur le canal de présence, mais pour ce faire, vous avez également besoin d'une configuration de minuterie côté client et vous devrez créer un fournisseur de services pour lelaravel-websockets
serveur comme ce problème github "Exister un moyen de implémenter des webhooks? " .Cela se produit car les temporisateurs clients arrêtent l'exécution en veille prolongée, ils continuent donc à partir de là où ils étaient auparavant. Mais si vous utilisez une variable de date pour gagner du temps , cette variable ne sera pas mise à jour lorsque l'ordinateur se mettra en veille prolongée, ainsi vous saurez quand il sortira du sommeil en vérifiant cette variable de date qui, par rapport à l'heure actuelle, aura une importance significative différence et sera supérieure à l'intervalle de la minuterie.
Implémentation de la logique temporelle dans le client
Vous pouvez également voir cette implémentation dans cette Q / R associée : des navigateurs de bureau peuvent-ils détecter le moment où l'ordinateur sort du mode veille?
Vous pouvez configurer une minuterie dans le client pour qu'elle s'exécute chaque minute. Nous ne compterons pas sur l'intervalle de temporisation , mais à la place, cette temporisation vérifiera une variable de date de portée externe si l'intervalle de temps depuis la dernière temporisation est supérieur à
15
minutes; si c'est le cas, cela signifie que le navigateur / JS a interrompu l'exécution pour une raison quelconque , éventuellement l'hibernation de l'appareil ( veille ), puis vous redirigez l'utilisateur vers la route de déconnexion.Exemple de code client JS:
Vous pouvez vérifier cet exemple simple mais en utilisant un. Il est préférable de le tester sur un ordinateur portable en fermant le couvercle, puis de l'ouvrir à nouveau après1
deuxième temporisateur avec15
déconnexion des secondes ici15 secondespar minute sur deux, car si vous avez de nombreux programmes en cours d'exécution, l'ordinateur prend un certain temps pour enregistrer l'état de la mémoire afin de terminer le mode d'hibernation et d'arrêter l'exécution.Exemple de travailleurs Web
Vous pouvez même utiliser l' API Web Workers pour configurer un travailleur Web pour qu'il soit beaucoup plus sûr:
Page JS code:
logoutWorker.js
Code de travail Web :Vous pouvez également consulter l'exemple Web Worker avec la même
15
minuterie de secondes ici .la source
clientSession
variable. Vous pouvez vérifier ma réponse à nouveau, j'ai même ajouté un exemple de Web Worker.?v=1
à la fin.Tout d'abord, expliquons pourquoi les sites Web bancaires vous déconnectent après 15 minutes sans activité. C'est une exigence PCI pour la sécurité.
Exigence PCI-DSS 8.1.8 :
Pour y parvenir, la solution est en réalité beaucoup plus primitive que vous ne l'imaginez . Il ne nécessite ni l'utilisation de websockets ni aucune connaissance de l'état de la machine du client (veille ou éveillé ou autre). Il suffit de connaître l'intervalle de temps entre la demande en cours utilisant cette session et la dernière demande en utilisant la même session et de s'assurer qu'ils ne sont pas distants de plus de 15 minutes. S'ils le sont, l'utilisateur doit être réauthentifié. Si ce n'est pas le cas, vous pouvez poursuivre la demande.
Le message "session expirée"
Vous vous demandez probablement (si c'est aussi simple) comment le message de délai d'expiration de la session apparaît lorsque vous mettez l'ordinateur en veille et le réveillez. Cette partie est d'une simplicité trompeuse.
Lorsque l'ordinateur est mis en veille, le navigateur déconnecte réellement toutes les connexions TCP / IP, ce qui à son tour arrête la boucle d'événements dans le moteur javascript. Les minuteries ne fonctionnent donc pas. Mais lorsque le navigateur se réveille à nouveau, il tente de rafraîchir certaines choses, y compris la page elle-même. Ainsi, lorsque la page est actualisée, la demande revient au serveur invoquant le serveur pour demander à l'utilisateur de s'authentifier à nouveau.
Cependant, cela ne tiendra pas compte du mode de message javascript (si c'est ce à quoi vous faites référence) que font certains sites bancaires. De plus, tous les navigateurs n'effectuent pas une actualisation matérielle de la page dans tous les scénarios. Une autre approche peut donc être adoptée. Plutôt que d'avoir un minuteur dans le navigateur qui expire après 15 minutes, vous pouvez simplement stocker le temps de chargement de la page en javascript comme horodatage et avoir un intervalle de 1 seconde qui compare cet horodatage à l'horodatage actuel de l'ordinateur. S'ils sont espacés de plus de 15 minutes, la session doit être terminée.
Même si l'ordinateur se met en veille et que le minuteur s'arrête, la session finira par expirer du côté serveur ( voir la section ci-dessous pour plus de détails ) et lorsque l'ordinateur se réveillera à nouveau, le minuteur avec un intervalle de 1 seconde redémarrera finalement, invoquant le (comme si l'utilisateur avait expiré pendant que l'ordinateur dormait). Le temps perdu entre le moment où l'ordinateur s'est endormi et le moment où l'ordinateur se réveille n'a pas d'importance car l'horodatage restera en mémoire. La déconnexion entre le client et le serveur est sans importance car ils n'ont pas besoin de communiquer ces informations pour que la session se termine correctement côté serveur. Le serveur peut effectuer sa propre récupération de place et terminer la session sans communication du client (c'est-à-dire de manière asynchrone ).
Croyez-le ou non, les banques ne se soucient pas de l'activité à l'intérieur du client. Ils ne se soucient que de l'activité de demande au serveur. Donc, si vous vous demandez comment gardent-ils la session vivante pendant plus de 15 minutes lorsque l'utilisateur est sur la même page pendant aussi longtemps, ils envoient simplement une demande AJAX en arrière-plan pour actualiser la session après avoir demandé à l'utilisateur s'ils continuent veulent continuer.
Cela peut être fait dans le même
onload
rappel d'événement que nous avons utilisé précédemment, comme ceci:Gestion de la fin de session côté serveur
Pour gérer la fin de session côté serveur, il existe plusieurs approches. Selon celui que vous utilisez, vous aurez besoin de différentes tactiques. La première consiste à utiliser le gestionnaire de session par défaut de PHP et à définir l'
session.max_lifetime
expiration après 15 minutes (cela supprime les données de session entièrement côté serveur, ce qui invalide le cookie du client).Si vous laissez le mécanisme de gestionnaire de session par défaut le faire, vous pouvez rencontrer des problèmes en fonction du gestionnaire utilisé (fichiers, memcached, redis, custom, etc.).
Avec les fichiers (gestionnaire par défaut), le garbage collection se déroule de deux manières:
session.max_lifetime
. Le problème avec cette approche est que sur les sites à faible trafic, une session peut potentiellement rester longtemps sur le serveur jusqu'à ce que suffisamment de demandes arrivent (en fonction dusession.gc_probability
score) pour invoquer le GC pour nettoyer les fichiers de session.Avec les gestionnaires basés sur memcached et redis, vous n'avez pas ce problème. Ils géreront automatiquement la purge de la mémoire. Les sessions peuvent toujours rester dans la mémoire physique pendant un certain temps au-delà de leur durée de vie, mais le démon ne pourra pas y accéder. Si vous êtes préoccupé par ce bit pour la sécurité, vous pouvez crypter vos sessions au repos ou trouver un magasin de clés / valeurs doté d'un mécanisme GC de purge de mémoire plus strict.
Avec un gestionnaire de session personnalisé, vous devrez créer votre propre mécanisme GC. Grâce à
SessionHandlerInterface
vous souhaitez mettre en œuvre unegc
méthode qui vous la main intervalle de durée de vie maximale de la session et que vous seriez responsable de vérifier si la session a dépassé sa durée de vie en fonction de cet intervalle et faites votre collecte des ordures à partir de là.Vous pouvez également configurer un point de terminaison distinct qui vérifie le TTL de la session (via une demande AJAX asynchrone côté client) et renvoie une réponse si la session a expiré (forçant le javascript à ré-authentifier l'utilisateur).
la source
Donc, Idea est derrière setInterval et Sockets, setInterval est pris en charge dans la plupart des navigateurs et javascript WbsocketApi est pris en charge dans presque tous les navigateurs.
Bref aperçu: setInterval () - ce comportement de fonction suit lorsque votre ordinateur est en mode veille / suspendu / hibernation il est en pause et lorsque vous êtes en mode réveil, il reprend lui-même.
Le code suivant fait ce qui suit, au début (peut-être en même temps mais) il démarre php server_socket en écoutant les connexions,
que javascript websocket api envoie l'horodatage actuel en horodatage Unix millisecondes toutes les 2 secondes, vous pouvez avoir 1 seconde, c'est à vous.
après que le socket du serveur php obtient cette fois-ci et vérifie s'il a quelque chose comme l'heure précédente à comparer, lorsque le code est instancié pour la première fois, php n'a rien de semblable à l'heure précédente pour le comparer à l'heure qui a été envoyée à partir de websocket javascript, donc php ne fait rien mais enregistre ce temps dans la session appelée «prev_time» et attend qu'une autre donnée de temps soit reçue de la socket javascript, donc ici commence le deuxième cycle. lorsque le serveur php charge de nouvelles données temporelles de javascript WebsocketApi, il vérifie qu'il a quelque chose comme l'heure précédente à comparer à ces données temporelles nouvellement reçues, cela signifie que php vérifie si une session appelée 'prev_time' existe, car nous sommes dans le deuxième cycle php découvre que il existe, saisit sa valeur et suit
$diff = $new_time - $prev_time
, $ diff sera de 2 secondes ou 2000 millisecondes, car n'oubliez pas que notre cycle setInterval se produit toutes les 2 secondes et que le format d'heure que nous envoyons est en millisecondes,que php vérifie
if($diff<3000)
si la différence est inférieure à 3000 si elle sait que l'utilisateur est actif, encore une fois vous pouvez manipuler ces secondes comme vous le souhaitez, je choisis 3000 car une latence possible dans le réseau qui est presque impossible mais vous savez que je suis toujours prudent quand il s'agit de réseaux, alors continuons, lorsque php détermine que l'utilisateur est actif, php réinitialise simplement la session 'prev_time' avec la valeur$new_time
qui vient d'être reçue et juste à des fins de test, il renvoie un message à la socket javascript,mais s'il
$diff
est supérieur à 3000, cela signifie que quelque chose a interrompu notre setInterval et qu'il n'y a qu'une seule façon que cela puisse se produire et je pense que vous savez déjà ce que je dis, donc dans laelse
logique de (if($diff<3000)
) vous pouvez déconnecter l'utilisateur en détruisant une session spécifique et si vous voulez rediriger vous pouvez envoyer du texte à la socket javacript et créer une logique qui s'exécutera enwindow.location = "/login"
fonction du texte, ça y est voici le code:D'abord c'est le fichier index.html juste pour charger javascript:
alors c'est javascript, il n'est pas vraiment joliment codé mais vous pouvez comprendre LIRE LES COMMENTAIRES ILS SONT IMPORTANT:
maintenant, voici une partie du code php, ne vous inquiétez pas, il y a aussi du code complet, mais cette partie est en fait ce qui fait les travaux mentionnés ci-dessus, vous rencontrerez également d'autres fonctions, mais elles sont destinées au décodage et au travail avec des sockets javascript, donc c'est vraiment la bonne chose LISEZ COMMENTAIRES ILS SONT IMPORTANTS:
Et voici le code complet de php:
NOTE LISEZ-LE: la
$new_time
variable est$jsTime
dans le codecréez un dossier et copiez et collez-le dans des fichiers exécutez php socket avec la commande: php -f server_socket.php allez sur l'hôte local et testez-le pour ouvrir la console pour voir les messages, il dira "vous êtes actif" ou "vous n'êtes pas actif" (quand tu reviens du sommeil); votre exécutable se produira lorsque l'utilisateur sortira du sommeil et non lorsqu'il sera en veille car à ce moment tout est mis en cache dans le fichier d'échange (windows) ou dans swap (linux)
la source
Je pense que j'ai une idée, vous avez beaucoup discuté du fonctionnement du système de connexion / déconnexion bancaire.
Cas 1: accès de la page Web à l'utilisateur pour une durée illimitée si l'utilisateur est actif
Chaque fois qu'un utilisateur s'est connecté, démarrez une minuterie sur votre backend (définissez la limite de temps comme vous le souhaitez), disons 15 minutes. Maintenant qu'est-ce que cela signifie ?? Cela signifie que si l'utilisateur n'effectue aucune activité sur la page Web, nous le déconnecterons.
Maintenant, de l'avant, vous pouvez envoyer l'activité de l'utilisateur à votre backend (peut être envoyée à l'aide d'une socket ou d'une longue interrogation), ce qui réinitialisera essentiellement le minuteur et l'utilisateur pourra utiliser la page Web activement pendant la durée de son choix.
Si l'utilisateur met son PC en veille, le minuteur ne se réinitialise pas et vous pouvez invalider la session une fois le minuteur terminé.
Si vous souhaitez invalider la session utilisateur dès qu'ils mettent leur PC en veille, vous pouvez définir la limite de la durée de validation de la session. Par exemple, lorsque l'utilisateur se connecte, nous créons la session qui ne sera valide que pendant 10 secondes, et une fois que nous recevons la demande d'activité de l'utilisateur, nous pouvons réinitialiser le minuteur et fournir une nouvelle clé de session.
J'espère que ceci vous aide. Faites-moi savoir si vous avez des questions.
la source
J'ai écrit un script pour détecter si la machine s'est endormie. L'idée est que lorsque la machine est en mode veille, tous les scripts s'arrêtent. Par conséquent, si nous gardons une trace de l'heure actuelle dans un intervalle de temps. Chaque fois que timeInterval déclenche l'heure actuelle moins (-), la nouvelle heure doit être suffisamment proche de timeInterval. Par conséquent, si nous voulons vérifier si la minuterie était inactive pendant le temps X, nous pouvons vérifier si la différence de temps est supérieure à X.
Un exemple vérifie si l'ordinateur a été mis en veille pendant plus de 15 secondes. Veuillez noter que lorsque vous mettez l'ordinateur en veille, il faut environ 15 secondes supplémentaires pour concevoir tous les processeurs. (Lors d'un test sur MON PC).
la source
J'ai implémenté exactement la même exigence en utilisant AWS Cognito, avec Lambda Authorizers, & Redis, je ne peux pas partager le code à ce stade mais je peux vous dire tout comment il est implémenté avec ces composants, les mêmes concepts peuvent être utilisés avec d'autres composants non AWS.
Tout d'abord avec l'implémentation d'une déconnexion d'inactivité, vous devrez le faire côté serveur, comme si quelqu'un éteignait simplement son ordinateur, le site Web frontal ne les déconnecterait pas. J'ai utilisé le concept d'
ACTIVE
utilisateurs. Lorsque les utilisateurs s'authentifient avec succès, je stocke avec un TTL de 15 minutes dans Redis une entrée avec une clé de leurusername
et une valeur deACTIVE
(il peut s'agir de nom d'utilisateur + identifiant de session si vous souhaitez autoriser plusieurs sessions pour un utilisateur donné en même temps).Dans mes Authorizers personnalisés lorsqu'un utilisateur est
ACTIVE
& ils ont un Token valide, je leur accorde l'accès à la ressource protégée ET surtout, je fais une autre mise dans Redis avec leusername
&ACTIVE
.Chaque fois que l'utilisateur se déconnecte, je le déconnecte dans ma solution de gestion des identités (Cognito) et je le marque comme
INACTIVE
. Notez que si un utilisateur n'atteint pas l'API dans les 15 minutes, il n'aura plus d'entréeACTIVE
contre son nom d'utilisateur et ne pourra plus accéder à l'API et devra se reconnecter, ce pour quoi il sera redirigé.Il y a beaucoup de choses à considérer avec cette approche, pour une chose souvent, les Autorisateurs mettent en cache les résultats pendant un certain temps, et si par exemple vous cachez le résultat pendant 5 minutes à titre d'exemple, votre utilisateur peut être déconnecté en 10 minutes en tant qu'utilisateur pourrait atteindre le cache au lieu de l'autorisateur qui ne rafraîchirait pas l'
ACTIVE
entrée.Il est également important de vous assurer que tout ce que vous utilisez pour stocker si un utilisateur donné
ACTIVE
est hautement disponible et récupérera rapidement en cas de défaillance.L'approche de l'utilisation d'une mémoire cache de cette manière est similaire à la façon dont l'invalidation de jeton est mise à niveau vers des protocoles d'autorisation sans état tels que OAuth2.
Nous utilisons cette approche depuis quelques mois maintenant, cela semble bien fonctionner pour nous, cela peut être un peu gênant à gérer, je m'attendais à AWS world qu'il y aurait un prêt à utiliser de la solution de boîte pour cela, mais il n'y en avait pas de parler.
la source