Déconnexion automatique avec Angularjs basée sur l'utilisateur inactif

Est-il possible de déterminer si un utilisateur est inactif et de connecter automatiquement après 10 minutes d'inactivité en utilisant angularjs?

j'essayais d'éviter d'utiliser jQuery, mais je ne trouve pas de tutoriels ou d'articles sur la façon de le faire à angularjs. Toute aide serait appréciée.

73
demandé sur georgeawg 2013-10-04 00:08:47

10 réponses

j'ai écrit un module appelé Ng-Idle qui peut vous être utile dans cette situation. Voici la page qui contient des instructions et une démo.

essentiellement, il a un service qui démarre un minuteur pour votre durée de ralenti qui peut être perturbé par l'activité de l'utilisateur (événements, tels que le clic, le défilement, la dactylographie). Vous pouvez également interrompre manuellement le délai en appelant une méthode sur le service. Si le délai n'est pas perturbé, alors il compte un avertissement où vous pouvez alerter l'utilisateur, ils vont être connecté. S'ils ne répondent pas après que le compte à rebours d'avertissement atteint 0, un événement est diffusé auquel votre application peut répondre. Dans votre cas, il pourrait émettre une demande pour tuer leur session et rediriger vers une page de connexion.

en outre, il a un service de garder-vivant qui peut ping une certaine URL à un intervalle. Il peut être utilisé par votre application pour garder la session d'un utilisateur en vie pendant qu'ils sont actifs. Le service au ralenti par par défaut, il s'intègre au service keep-alive, suspendant les pingings s'ils deviennent inactifs, et les reprenant à leur retour.

toutes les informations dont vous avez besoin pour commencer est sur le site avec plus de détails dans le wiki . Cependant, voici un morceau de config montrant comment les signer dehors quand ils time out.

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
    });
});
109
répondu HackedByChinese 2015-10-09 19:25:54

Vue Démo qui est à l'aide de angularjs et de voir votre navigateur du journal

<!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>

ci-dessous code est pur 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>

UPDATE

j'ai mis à jour ma solution comme suggestion @Tom.

<!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>

Cliquez ici pour voir à Plunker la mise à jour de la version

20
répondu Frank Myat Thu 2015-10-21 02:50:45

il devrait y avoir différentes façons de le faire et chaque approche devrait convenir mieux à une application particulière qu'à une autre. Pour la plupart des applications, vous pouvez simplement gérer les événements clés ou souris et activer/désactiver une minuterie logout de manière appropriée. Cela dit, sur le dessus de ma tête, une solution AngularJS-y "de fantaisie" est de surveiller la boucle de digestion, si aucune n'a été déclenchée pour la dernière [durée spécifiée] puis Déconnexion. Quelque chose comme cela.

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;
  });
});
18
répondu Buu Nguyen 2013-10-03 21:24:20

a joué avec L'approche de Boo, cependant n'aimez pas le fait que l'Utilisateur a été viré seulement une fois qu'un autre digest est lancé, ce qui signifie que l'utilisateur reste connecté jusqu'à ce qu'il essaie de faire quelque chose dans la page, puis immédiatement lancé.

j'essaie de forcer la déconnexion en utilisant un intervalle qui vérifie chaque minute si le dernier temps d'action était il y a plus de 30 minutes. J'ai accroché sur $routeChangeStart, mais pourrait aussi être accroché sur $rootScope.$montre que dans Boo exemple.

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();  
    });
});
11
répondu v-tec 2015-06-26 20:26:25

vous pourriez également accomplir en utilisant angular-activity-monitor d'une manière plus simple que l'injection de plusieurs fournisseurs et il utilise setInterval() (vs. angular's $interval ) pour éviter de déclencher manuellement une boucle de digestion (ce qui est important pour éviter de garder les articles vivants involontairement).

finalement, vous vous abonnez à quelques événements qui déterminent quand un utilisateur est inactif ou devient proche. Donc si vous voulez déconnecter un utilisateur après 10 procès-verbal d'inactivité, vous pouvez utiliser l'extrait suivant:

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
  });
}
5
répondu Sean3z 2016-01-19 13:57:10

j'ai essayé L'approche de Buu et je n'ai pas pu l'obtenir tout à fait correctement en raison du nombre d'événements qui déclenchent le digesteur à exécuter, y compris $interval et $timeout functions executing. Cela laisse l'application dans un état où il ne sera jamais inactif indépendamment de l'entrée de l'utilisateur.

si vous avez réellement besoin de suivre le temps d'inactivité de l'Utilisateur, Je ne suis pas sûr qu'il ya une bonne approche angulaire. Je suggère qu'une meilleure approche est représentée par Witoldz ici https://github.com/witoldsz/angular-http-auth . Cette approche incitera l'utilisateur à réauthenticate lorsqu'une action est prise qui nécessite ses justificatifs d'identité. Après que l'Utilisateur a authentifié la précédente requête manquée est retraité et l'application continue comme si rien ne s'était passé.

cela gère la préoccupation que vous pourriez avoir de laisser la session de l'utilisateur expirer pendant qu'ils sont actifs car même si leur authentification expire ils sont toujours en mesure pour conserver l'état de la demande et de ne pas perdre de travail.

si vous avez une session quelconque sur votre client (cookies, tokens, etc), Vous pouvez les surveiller et déclencher votre processus de déconnexion s'ils expirent.

app.run(['$interval', function($interval) {
  $interval(function() {
    if (/* session still exists */) {
    } else {
      // log out of client
    }
  }, 1000);
}]);

mise à jour: Voici un morceau qui démontre la préoccupation. http://plnkr.co/edit/ELotD8W8VAeQfbYFin1W . Ce que cela démontre, c'est que le temps d'exécution du digesteur est mis à jour seulement lorsque l'intervalle s'écoule. Une fois l' intervalle atteint max comte l'autoclave ne fonctionne plus.

2
répondu Seth M. 2014-01-17 15:57:55

ng-Idle ressemble à la façon d'aller, mais je n'ai pas pu comprendre les modifications de Brian F et a voulu timeout pour une session de sommeil trop, aussi j'avais un cas d'utilisation assez simple à l'esprit. Je pared vers le bas pour le code ci-dessous. Il hooks événements pour réinitialiser un indicateur de timeout (paresseusement placé dans $rootScope). Il ne détecte le timeout est arrivé que lorsque l'utilisateur revient (et déclenche un événement), mais c'est assez bon pour moi. Je ne pouvais pas obtenir l'emplacement d'angular $pour travailler ici, mais encore une fois, en utilisant document.emplacement.href fait son travail.

j'ai mis ça dans mon application.js après le .config a exécuter.

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
        }
    }
});
2
répondu James Bell 2014-04-05 15:57:11

je voudrais étendre les réponses à qui pourrait utiliser cela dans un plus grand projet, vous pourriez accidentellement attacher plusieurs gestionnaires d'événements et le programme se comporterait bizarrement.

pour me débarrasser de cela, j'ai utilisé une fonction singleton exposée par une usine, à partir de laquelle vous appelleriez inactivityTimeoutFactory.switchTimeoutOn() et inactivityTimeoutFactory.switchTimeoutOff() dans votre application angulaire pour respectivement activer et désactiver la déconnexion en raison de la fonctionnalité d'inactivité.

par ici. vous vous assurez que vous n'exécutez qu'une seule instance des gestionnaires d'événements, peu importe le nombre de fois que vous essayez d'activer la procédure de timeout, ce qui le rend plus facile à utiliser dans les applications où l'utilisateur pourrait se connecter à partir de différentes routes.

Voici mon 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();
    }
  };

}
1
répondu GChamon 2017-12-21 15:00:05

je pense que la montre digest cycle de Buu est géniale. Merci pour le partage. Comme d'autres l'ont fait remarquer, $interval fait aussi fonctionner le cycle de digest. Nous pourrions pour le but d'auto logging l'utilisateur hors utiliser setInterval qui ne causera pas une boucle digest.

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();
    });
});
0
répondu Declan McLaughlin 2015-09-24 07:03:32

j'ai utilisé ng-idle pour cela et j'ai ajouté un petit logout et token null code et ça fonctionne très bien, vous pouvez essayer ceci. Merci @HackedByChinese pour avoir fait un si beau module.

dans IdleTimeout je viens de supprimer mes données de session et token.

Voici mon code

$scope.$on('IdleTimeout', function () {
        closeModals();
        delete $window.sessionStorage.token;
        $state.go("login");
        $scope.timedout = $uibModal.open({
            templateUrl: 'timedout-dialog.html',
            windowClass: 'modal-danger'
        });
    });
0
répondu Sameer Khan 2017-03-03 04:34:31