web-dev-qa-db-ja.com

アイドルユーザーに基づいたAngularjsによる自動ログアウト

ユーザーが非アクティブであるかどうかを判断し、angularjsを使用して10分間非アクティブになった後、自動的にログアウトすることは可能ですか?

JQueryの使用を避けようとしましたが、angularjsでこれを行う方法に関するチュートリアルや記事を見つけることができません。任意の助けをいただければ幸いです。

77
user2101411

この状況であなたに役立つかもしれないNg-Idleというモジュールを書きました。 手順とデモを含むページです

基本的に、ユーザーのアクティビティ(クリック、スクロール、入力などのイベント)によって中断される可能性のあるアイドル期間のタイマーを開始するサービスがあります。サービスのメソッドを呼び出すことにより、タイムアウトを手動で中断することもできます。タイムアウトが中断されない場合、ログアウトされるユーザーに警告できる警告がカウントダウンされます。警告のカウントダウンが0に達した後に応答しない場合、アプリケーションが応答できるイベントがブロードキャストされます。あなたの場合、セッションを終了してログインページにリダイレクトするリクエストを発行できます。

さらに、一定の間隔でいくつかのURLをpingできるキープアライブサービスがあります。これは、ユーザーがアクティブな間、ユーザーのセッションを維持するためにアプリで使用できます。デフォルトでは、アイドルサービスはキープアライブサービスと統合され、アイドル状態になった場合はpingを一時停止し、戻ったときに再開します。

始めるために必要な情報はすべて site にあり、詳細は wiki にあります。ただし、タイムアウト時にログアウトする方法を示す構成のスニペットがあります。

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

表示 デモangularjsを使用しており、ブラウザのログを確認します

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

以下のコードは純粋なJavaScriptバージョンです

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

更新

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

更新されたバージョンのPlunkerを見るにはここをクリック

20
Frank Myat Thu

それを行うにはさまざまな方法が必要であり、各アプローチは特定のアプリケーションに他のアプリケーションよりも適している必要があります。ほとんどのアプリでは、単にキーまたはマウスイベントを処理し、ログアウトタイマーを適切に有効/無効にすることができます。とはいえ、私の頭上では、最後の[指定された期間]に何もトリガーされなかった場合にログアウトするダイジェストループを「派手な」AngularJS-yソリューションが監視しています。このようなもの。

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;
  });
});
19
Buu Nguyen

Booのアプローチを試しましたが、ユーザーが別のダイジェストが実行されるとキックオフされるという事実は好きではありません。つまり、ユーザーはページ内で何かを実行しようとするまでログインしたまま、すぐにキックオフされます。

最後のアクション時間が30分以上前かどうかを毎分チェックする間隔を使用して、強制的にログオフを試みています。 $ routeChangeStartにフックしましたが、Booの例のように$ rootScope。$ watchにフックすることもできます。

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
v-tec

angular-activity-monitor を使用して複数のプロバイダーを注入するよりも簡単な方法で達成することもできます。また、setInterval()(vs. angleの$interval)を使用して、ダイジェストループを手動でトリガーすることを回避します(アイテムが意図せずに生き続けることを防ぐことが重要です) )。

最終的には、ユーザーが非アクティブまたは親密になる時期を決定するいくつかのイベントをサブスクライブするだけです。したがって、非アクティブな状態が10分間続いた後にユーザーをログアウトする場合は、次のスニペットを使用できます。

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
Sean3z

私はBuuのアプローチを試してみましたが、$ intervalや$ timeout関数の実行など、ダイジェスターの実行をトリガーするイベントの数が非常に多いため、正しい方法を見つけることができませんでした。これにより、ユーザーの入力に関係なく、アプリケーションはアイドル状態になりません。

実際にユーザーのアイドル時間を追跡する必要がある場合、良いangularアプローチがあるかどうかはわかりません。ここでは https://github.com/witoldsz/angular-http-auth のWitoldzでより良いアプローチを表すことをお勧めします。このアプローチでは、資格情報を必要とするアクションが実行されたときに、ユーザーに再認証を求めます。ユーザーが認証された後、以前に失敗したリクエストが再処理され、何も起こらなかったようにアプリケーションが続行されます。

これにより、認証が期限切れになってもアプリケーションの状態を保持し、作業を失うことがないため、ユーザーがアクティブな間、ユーザーのセッションを期限切れにする懸念があります。

クライアントで何らかの種類のセッション(Cookie、トークンなど)を使用している場合は、それらも監視し、有効期限が切れるとログアウトプロセスをトリガーできます。

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

更新:ここに懸念を示すプランクがあります。 http://plnkr.co/edit/ELotD8W8VAeQfbYFin1W 。これが実証しているのは、ダイジェスターの実行時間が更新されるのは、間隔がカチカチになったときだけだということです。間隔が最大数に達すると、ダイジェスターは実行されなくなります。

3
Seth M.

ng-Idleは進むべき道のように見えますが、ブライアンFの変更を理解できず、スリープ状態のセッションのタイムアウトも望んでいました。また、非常にシンプルなユースケースを念頭に置いていました。私はそれを以下のコードにまとめました。イベントをフックしてタイムアウトフラグをリセットします(遅延して$ rootScopeに配置されます)。ユーザーが戻ったとき(およびイベントをトリガーしたとき)にタイムアウトが発生したことを検出するだけですが、それで十分です。ここでは、Angularの$ locationを機能させることはできませんでしたが、document.location.hrefを使用して作業を完了します。

.configの実行後に、これをapp.jsに貼り付けました。

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
        }
    }
});
3
James Bell

ブウのダイジェストサイクルウォッチは天才だと思います。共有してくれてありがとう。他の人が指摘しているように、$ intervalもダイジェストサイクルを実行します。ユーザーを自動ログアウトするために、ダイジェストループを引き起こさないsetIntervalを使用できます。

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

大規模なプロジェクトでこれを使用している人への回答を拡大したいと思います。誤って複数のイベントハンドラーをアタッチすると、プログラムの動作がおかしくなります。

それを取り除くために、ファクトリによって公開されたシングルトン関数を使用しました。この関数から、angularアプリケーションでinactivityTimeoutFactory.switchTimeoutOn()およびinactivityTimeoutFactory.switchTimeoutOff()を呼び出し、それぞれログアウトを有効化および無効化します。非アクティブ機能に。

これにより、タイムアウトプロシージャを何度アクティブにしようとしても、イベントハンドラーの単一インスタンスのみを実行していることを確認し、ユーザーが異なるルートからログインする可能性のあるアプリケーションで使いやすくします。

ここに私のコードがあります:

'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
GChamon

私はこれにng-idleを使用し、ログアウトとトークンのヌルコードを少し追加しましたが、うまく機能しています。これを試してみてください。このような素晴らしいモジュールを作成してくれた@HackedByChineseに感謝します。

IdleTimeoutでセッションデータとトークンを削除しました。

ここに私のコードがあります

$scope.$on('IdleTimeout', function () {
        closeModals();
        delete $window.sessionStorage.token;
        $state.go("login");
        $scope.timedout = $uibModal.open({
            templateUrl: 'timedout-dialog.html',
            windowClass: 'modal-danger'
        });
    });
1
Sameer Khan