Ist es möglich festzustellen, ob ein Benutzer inaktiv ist, und ihn nach etwa 10 Minuten Inaktivität mithilfe von anglejs automatisch abzumelden?
Ich habe versucht, die Verwendung von jQuery zu vermeiden, aber ich kann keine Tutorials oder Artikel dazu finden, wie dies in anglejs gemacht wird. Jede Hilfe wäre dankbar.
Antworten:
Ich habe ein Modul namens geschrieben
Ng-Idle
, das Ihnen in dieser Situation nützlich sein kann. Hier ist die Seite, die Anweisungen und eine Demo enthält.Grundsätzlich verfügt es über einen Dienst, der einen Timer für Ihre Leerlaufdauer startet, der durch Benutzeraktivitäten (Ereignisse wie Klicken, Scrollen, Tippen) unterbrochen werden kann. Sie können das Zeitlimit auch manuell unterbrechen, indem Sie eine Methode für den Dienst aufrufen. Wenn das Zeitlimit nicht unterbrochen wird, zählt es eine Warnung herunter, in der Sie den Benutzer benachrichtigen können, dass er abgemeldet wird. Wenn sie nicht antworten, nachdem der Warnungs-Countdown 0 erreicht hat, wird ein Ereignis gesendet, auf das Ihre Anwendung reagieren kann. In Ihrem Fall kann eine Anforderung ausgegeben werden, die Sitzung zu beenden und auf eine Anmeldeseite umzuleiten.
Darüber hinaus verfügt es über einen Keep-Alive-Dienst, der in bestimmten Abständen eine URL anpingen kann. Dies kann von Ihrer App verwendet werden, um die Sitzung eines Benutzers am Leben zu erhalten, während dieser aktiv ist. Der inaktive Dienst wird standardmäßig in den Keep-Alive-Dienst integriert, wobei der Ping-Vorgang unterbrochen wird, wenn er inaktiv wird, und bei seiner Rückkehr fortgesetzt wird.
Alle Informationen, die Sie für den Einstieg benötigen, finden Sie auf der Website mit weiteren Details im Wiki . Hier ist jedoch ein Ausschnitt aus der Konfiguration, der zeigt, wie Sie sie abmelden, wenn sie eine Zeitüberschreitung aufweisen.
angular.module('demo', ['ngIdle']) // omitted for brevity .config(function(IdleProvider, KeepaliveProvider) { IdleProvider.idle(10*60); // 10 minutes idle IdleProvider.timeout(30); // after 30 seconds idle, time the user out KeepaliveProvider.interval(5*60); // 5 minute keep-alive ping }) .run(function($rootScope) { $rootScope.$on('IdleTimeout', function() { // end their session and redirect to login }); });
quelle
Idle.watch()
die.run()
Funktion hinzugefügt habe . DasIdleTimeout
Ereignis wurde überhaupt nicht ausgelöst, bis ich das getan habe. Ich habe den AnrufIdle.watch()
auf Ihrer Github-Demo gesehen, also habe ich ihn von dort bekommen.Ansicht Demo , die verwendet
angularjs
und Ihre Browser - Log sehen<!DOCTYPE html> <html ng-app="Application_TimeOut"> <head> <script src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular.min.js"></script> </head> <body> </body> <script> var app = angular.module('Application_TimeOut', []); app.run(function($rootScope, $timeout, $document) { console.log('starting run'); // Timeout timer value var TimeOutTimerValue = 5000; // Start a timeout var TimeOut_Thread = $timeout(function(){ LogoutByTimer() } , TimeOutTimerValue); var bodyElement = angular.element($document); /// Keyboard Events bodyElement.bind('keydown', function (e) { TimeOut_Resetter(e) }); bodyElement.bind('keyup', function (e) { TimeOut_Resetter(e) }); /// Mouse Events bodyElement.bind('click', function (e) { TimeOut_Resetter(e) }); bodyElement.bind('mousemove', function (e) { TimeOut_Resetter(e) }); bodyElement.bind('DOMMouseScroll', function (e) { TimeOut_Resetter(e) }); bodyElement.bind('mousewheel', function (e) { TimeOut_Resetter(e) }); bodyElement.bind('mousedown', function (e) { TimeOut_Resetter(e) }); /// Touch Events bodyElement.bind('touchstart', function (e) { TimeOut_Resetter(e) }); bodyElement.bind('touchmove', function (e) { TimeOut_Resetter(e) }); /// Common Events bodyElement.bind('scroll', function (e) { TimeOut_Resetter(e) }); bodyElement.bind('focus', function (e) { TimeOut_Resetter(e) }); function LogoutByTimer() { console.log('Logout'); /////////////////////////////////////////////////// /// redirect to another page(eg. Login.html) here /////////////////////////////////////////////////// } function TimeOut_Resetter(e) { console.log('' + e); /// Stop the pending timeout $timeout.cancel(TimeOut_Thread); /// Reset the timeout TimeOut_Thread = $timeout(function(){ LogoutByTimer() } , TimeOutTimerValue); } }) </script> </html>
Der folgende Code ist eine reine Javascript-Version
<html> <head> <script type="text/javascript"> function logout(){ console.log('Logout'); } function onInactive(millisecond, callback){ var wait = setTimeout(callback, millisecond); document.onmousemove = document.mousedown = document.mouseup = document.onkeydown = document.onkeyup = document.focus = function(){ clearTimeout(wait); wait = setTimeout(callback, millisecond); }; } </script> </head> <body onload="onInactive(5000, logout);"></body> </html>
AKTUALISIEREN
Ich habe meine Lösung als @ Tom-Vorschlag aktualisiert.
<!DOCTYPE html> <html ng-app="Application_TimeOut"> <head> <script src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular.min.js"></script> </head> <body> </body> <script> var app = angular.module('Application_TimeOut', []); app.run(function($rootScope, $timeout, $document) { console.log('starting run'); // Timeout timer value var TimeOutTimerValue = 5000; // Start a timeout var TimeOut_Thread = $timeout(function(){ LogoutByTimer() } , TimeOutTimerValue); var bodyElement = angular.element($document); angular.forEach(['keydown', 'keyup', 'click', 'mousemove', 'DOMMouseScroll', 'mousewheel', 'mousedown', 'touchstart', 'touchmove', 'scroll', 'focus'], function(EventName) { bodyElement.bind(EventName, function (e) { TimeOut_Resetter(e) }); }); function LogoutByTimer(){ console.log('Logout'); /////////////////////////////////////////////////// /// redirect to another page(eg. Login.html) here /////////////////////////////////////////////////// } function TimeOut_Resetter(e){ console.log(' ' + e); /// Stop the pending timeout $timeout.cancel(TimeOut_Thread); /// Reset the timeout TimeOut_Thread = $timeout(function(){ LogoutByTimer() } , TimeOutTimerValue); } }) </script> </html>
Klicken Sie hier, um die aktualisierte Version bei Plunker anzuzeigen
quelle
bodyElement.on(...)
und innerhalbLogoutByTimer
ausführenbodyElement.off(...)
Es sollte verschiedene Möglichkeiten geben, und jeder Ansatz sollte besser zu einer bestimmten Anwendung passen als zu einer anderen. Bei den meisten Apps können Sie einfach nur Tasten- oder Mausereignisse verarbeiten und einen Abmelde-Timer entsprechend aktivieren / deaktivieren. Das heißt, auf der Oberseite meines Kopfes überwacht eine "ausgefallene" AngularJS-y-Lösung die Digest-Schleife, wenn während der letzten [angegebenen Dauer] keine ausgelöst wurde, dann melden Sie sich ab. Etwas wie das.
app.run(function($rootScope) { var lastDigestRun = new Date(); $rootScope.$watch(function detectIdle() { var now = new Date(); if (now - lastDigestRun > 10*60*60) { // logout here, like delete cookie, navigate to login ... } lastDigestRun = now; }); });
quelle
Gespielt mit Boos Ansatz, mag jedoch nicht die Tatsache, dass der Benutzer erst nach dem Ausführen eines anderen Digests gestartet wurde, was bedeutet, dass der Benutzer angemeldet bleibt, bis er versucht, etwas auf der Seite zu tun, und dann sofort gestartet wird.
Ich versuche, die Abmeldung mithilfe eines Intervalls zu erzwingen, das jede Minute überprüft, ob die letzte Aktionszeit mehr als 30 Minuten zurückliegt. Ich habe es an $ routeChangeStart angehängt, könnte aber auch an $ rootScope angehängt werden. $ Watch wie in Boos Beispiel.
app.run(function($rootScope, $location, $interval) { var lastDigestRun = Date.now(); var idleCheck = $interval(function() { var now = Date.now(); if (now - lastDigestRun > 30*60*1000) { // logout } }, 60*1000); $rootScope.$on('$routeChangeStart', function(evt) { lastDigestRun = Date.now(); }); });
quelle
$rootScope.$watch
in diesem Fall würde das Umschalten auf erfordernsetInterval
, da$interval
bei jedem Funktionsaufruf ein Digest ausgelöst wird, wodurch Ihr Digest effektiv zurückgesetzt wirdlastDigestRun
.Sie können die Verwendung auch
angular-activity-monitor
einfacher durchführen als das Injizieren mehrerer Anbieter, und es wirdsetInterval()
(im Vergleich zu Winkeln$interval
) verwendet, um das manuelle Auslösen einer Digest-Schleife zu vermeiden (was wichtig ist, um zu verhindern, dass Elemente unbeabsichtigt am Leben bleiben).Letztendlich abonnieren Sie nur einige Ereignisse, die bestimmen, wann ein Benutzer inaktiv ist oder sich nähert. Wenn Sie also einen Benutzer nach 10 Minuten Inaktivität abmelden möchten, können Sie das folgende Snippet verwenden:
angular.module('myModule', ['ActivityMonitor']); MyController.$inject = ['ActivityMonitor']; function MyController(ActivityMonitor) { // how long (in seconds) until user is considered inactive ActivityMonitor.options.inactive = 600; ActivityMonitor.on('inactive', function() { // user is considered inactive, logout etc. }); ActivityMonitor.on('keepAlive', function() { // items to keep alive in the background while user is active }); ActivityMonitor.on('warning', function() { // alert user when they're nearing inactivity }); }
quelle
Ich habe Buus Ansatz ausprobiert und konnte ihn aufgrund der schieren Anzahl von Ereignissen, die die Ausführung des Fermenters auslösen, einschließlich der Ausführung von $ interval- und $ timeout-Funktionen, nicht ganz richtig machen. Dadurch bleibt die Anwendung in einem Zustand, in dem sie unabhängig von Benutzereingaben niemals inaktiv ist.
Wenn Sie tatsächlich die Leerlaufzeit des Benutzers verfolgen müssen, bin ich mir nicht sicher, ob es einen guten Winkelansatz gibt. Ich würde vorschlagen, dass Witoldz hier https://github.com/witoldsz/angular-http-auth einen besseren Ansatz vertritt . Dieser Ansatz fordert den Benutzer auf, sich erneut zu authentifizieren, wenn eine Aktion ausgeführt wird, für die seine Anmeldeinformationen erforderlich sind. Nachdem sich der Benutzer authentifiziert hat, wird die vorherige fehlgeschlagene Anforderung erneut verarbeitet und die Anwendung wird fortgesetzt, als ob nichts passiert wäre.
Dies behebt das Problem, dass die Sitzung des Benutzers möglicherweise abläuft, während er aktiv ist, da er auch nach Ablauf seiner Authentifizierung den Anwendungsstatus beibehalten und keine Arbeit verlieren kann.
Wenn Sie eine Sitzung auf Ihrem Client haben (Cookies, Token usw.), können Sie diese ebenfalls ansehen und Ihren Abmeldevorgang auslösen, wenn sie ablaufen.
app.run(['$interval', function($interval) { $interval(function() { if (/* session still exists */) { } else { // log out of client } }, 1000); }]);
UPDATE: Hier ist ein Plunk, der die Besorgnis demonstriert. http://plnkr.co/edit/ELotD8W8VAeQfbYFin1W . Dies zeigt, dass die Laufzeit des Fermenters nur aktualisiert wird, wenn das Intervall aktiviert ist. Sobald das Intervall die maximale Anzahl erreicht hat, läuft der Fermenter nicht mehr.
quelle
ng-Idle sieht nach dem richtigen Weg aus, aber ich konnte die Modifikationen von Brian F nicht herausfinden und wollte auch eine Auszeit für eine Schlafsitzung nehmen. Außerdem hatte ich einen ziemlich einfachen Anwendungsfall im Sinn. Ich habe es auf den folgenden Code reduziert. Es bindet Ereignisse ein, um ein Timeout-Flag zurückzusetzen (träge in $ rootScope platziert). Es erkennt nur, dass das Timeout aufgetreten ist, wenn der Benutzer zurückkehrt (und ein Ereignis auslöst), aber das ist gut genug für mich. Ich konnte Angulars $ location nicht zum Laufen bringen, aber mit document.location.href wird der Job erledigt.
Ich habe dies in meine app.js gesteckt, nachdem die .config ausgeführt wurde.
app.run(function($rootScope,$document) { var d = new Date(); var n = d.getTime(); //n in ms $rootScope.idleEndTime = n+(20*60*1000); //set end time to 20 min from now $document.find('body').on('mousemove keydown DOMMouseScroll mousewheel mousedown touchstart', checkAndResetIdle); //monitor events function checkAndResetIdle() //user did something { var d = new Date(); var n = d.getTime(); //n in ms if (n>$rootScope.idleEndTime) { $document.find('body').off('mousemove keydown DOMMouseScroll mousewheel mousedown touchstart'); //un-monitor events //$location.search('IntendedURL',$location.absUrl()).path('/login'); //terminate by sending to login page document.location.href = 'https://whatever.com/myapp/#/login'; alert('Session ended due to inactivity'); } else { $rootScope.idleEndTime = n+(20*60*1000); //reset end time } } });
quelle
Ich denke, Buus Digest Cycle Watch ist genial. Danke für das Teilen. Wie andere angemerkt haben, führt $ interval auch dazu, dass der Digest-Zyklus ausgeführt wird. Wir könnten zum automatischen Abmelden des Benutzers setInterval verwenden, das keine Digest-Schleife verursacht.
app.run(function($rootScope) { var lastDigestRun = new Date(); setInterval(function () { var now = Date.now(); if (now - lastDigestRun > 10 * 60 * 1000) { //logout } }, 60 * 1000); $rootScope.$watch(function() { lastDigestRun = new Date(); }); });
quelle
Ich habe ng-idle dafür verwendet und ein wenig Logout- und Token-Null-Code hinzugefügt, und es funktioniert einwandfrei. Sie können dies versuchen. Vielen Dank an @HackedByChinese für das Erstellen eines so schönen Moduls.
In IdleTimeout habe ich gerade meine Sitzungsdaten und mein Token gelöscht.
$scope.$on('IdleTimeout', function () { closeModals(); delete $window.sessionStorage.token; $state.go("login"); $scope.timedout = $uibModal.open({ templateUrl: 'timedout-dialog.html', windowClass: 'modal-danger' }); });
quelle
Ich möchte die Antworten auf diejenigen erweitern, die dies in einem größeren Projekt verwenden. Sie könnten versehentlich mehrere Ereignishandler anhängen und das Programm würde sich seltsam verhalten.
Um dies zu beseitigen, habe ich eine Singleton-Funktion verwendet, die von einer Factory bereitgestellt wird, von der aus Sie
inactivityTimeoutFactory.switchTimeoutOn()
undinactivityTimeoutFactory.switchTimeoutOff()
in Ihrer Winkelanwendung die Abmeldung aufgrund von Inaktivitätsfunktionen aktivieren bzw. deaktivieren würden.Auf diese Weise stellen Sie sicher, dass Sie nur eine einzige Instanz der Ereignishandler ausführen, unabhängig davon, wie oft Sie versuchen, das Timeout-Verfahren zu aktivieren. Dies erleichtert die Verwendung in Anwendungen, in denen sich der Benutzer möglicherweise von verschiedenen Routen aus anmeldet.
Hier ist mein Code:
'use strict'; angular.module('YOURMODULENAME') .factory('inactivityTimeoutFactory', inactivityTimeoutFactory); inactivityTimeoutFactory.$inject = ['$document', '$timeout', '$state']; function inactivityTimeoutFactory($document, $timeout, $state) { function InactivityTimeout () { // singleton if (InactivityTimeout.prototype._singletonInstance) { return InactivityTimeout.prototype._singletonInstance; } InactivityTimeout.prototype._singletonInstance = this; // Timeout timer value const timeToLogoutMs = 15*1000*60; //15 minutes const timeToWarnMs = 13*1000*60; //13 minutes // variables let warningTimer; let timeoutTimer; let isRunning; function switchOn () { if (!isRunning) { switchEventHandlers("on"); startTimeout(); isRunning = true; } } function switchOff() { switchEventHandlers("off"); cancelTimersAndCloseMessages(); isRunning = false; } function resetTimeout() { cancelTimersAndCloseMessages(); // reset timeout threads startTimeout(); } function cancelTimersAndCloseMessages () { // stop any pending timeout $timeout.cancel(timeoutTimer); $timeout.cancel(warningTimer); // remember to close any messages } function startTimeout () { warningTimer = $timeout(processWarning, timeToWarnMs); timeoutTimer = $timeout(processLogout, timeToLogoutMs); } function processWarning() { // show warning using popup modules, toasters etc... } function processLogout() { // go to logout page. The state might differ from project to project $state.go('authentication.logout'); } function switchEventHandlers(toNewStatus) { const body = angular.element($document); const trackedEventsList = [ 'keydown', 'keyup', 'click', 'mousemove', 'DOMMouseScroll', 'mousewheel', 'mousedown', 'touchstart', 'touchmove', 'scroll', 'focus' ]; trackedEventsList.forEach((eventName) => { if (toNewStatus === 'off') { body.off(eventName, resetTimeout); } else if (toNewStatus === 'on') { body.on(eventName, resetTimeout); } }); } // expose switch methods this.switchOff = switchOff; this.switchOn = switchOn; } return { switchTimeoutOn () { (new InactivityTimeout()).switchOn(); }, switchTimeoutOff () { (new InactivityTimeout()).switchOff(); } }; }
quelle