유휴 사용자를 기반으로 Angularjs로 자동 로그 아웃


78

사용자가 비활성 상태인지 확인하고 angularjs를 사용하여 10 분 동안 비활성 상태가되면 자동으로 로그 아웃 할 수 있습니까?

jQuery를 사용하지 않으려 고했지만 angularjs에서이를 수행하는 방법에 대한 자습서 나 기사를 찾을 수 없습니다. 어떤 도움을 주시면 감사하겠습니다.



6
@Stewie 그는 ... jQuery를 피하려고 쓴
세바스찬

이 기능에 대한 옵션이 있습니까?
HP의 411

답변:


112

Ng-Idle이 상황에서 유용 할 수 있는 모듈을 작성했습니다 . 다음은 지침과 데모가 포함 된 페이지입니다.

기본적으로 사용자 활동 (클릭, 스크롤, 입력과 같은 이벤트)에 의해 중단 될 수있는 유휴 시간 동안 타이머를 시작하는 서비스가 있습니다. 서비스에서 메서드를 호출하여 시간 제한을 수동으로 중단 할 수도 있습니다. 시간 초과가 중단되지 않으면 사용자가 로그 아웃 될 것임을 경고 할 수있는 경고를 카운트 다운합니다. 경고 카운트 다운이 0에 도달 한 후에도 응답하지 않으면 애플리케이션이 응답 할 수있는 이벤트가 브로드 캐스팅됩니다. 귀하의 경우 세션을 종료하고 로그인 페이지로 리디렉션하라는 요청을 보낼 수 있습니다.

또한 일정 간격으로 일부 URL을 ping 할 수있는 연결 유지 서비스가 있습니다. 이것은 사용자가 활성 상태 인 동안 사용자의 세션을 유지하기 위해 앱에서 사용할 수 있습니다. 기본적으로 유휴 서비스는 연결 유지 서비스와 통합되어 유휴 상태가되면 핑을 일시 중단하고 돌아 오면 다시 시작합니다.

시작하는 데 필요한 모든 정보 는 위키의 자세한 내용과 함께 사이트 에 있습니다 . 그러나 다음은 시간 초과시 로그 아웃하는 방법을 보여주는 구성 스 니펫입니다.

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

2
헤이, 당신의 접근 방식은 페이지가 백그라운드로 들어갈 때 시간 초과가 일시 중지되는 모바일 사파리에서 문제가 발생할 때까지 완벽하게 작동했습니다. 모든 시계에 유휴 타임 스탬프를 설정하도록 수정해야했습니다. 인터럽트에 대한 모든 업데이트 전에 Safari 문제를 해결하는 idleTimeout이 만료되지 않았는지 확인합니다 (자동 로그 아웃되지 않지만 첫 번째 터치 / 마우스 / 클릭시 로그 아웃).
Brian F

@BrianF 흥미 롭군요. 당신이 기꺼이 할 수 있다면, 나는 당신의 변경 사항에 대한 풀 요청에 관심이 있거나 적어도 당신의 변경 사항에 대한 코드의 예와 함께 열린 문제에 관심이 있습니다.
moribvndvs 2014 년

github-github.com/brianfoody/Angular/blob/master/src/idle.js 에서 던진 샘플을 살펴보십시오 . 나는 카운트 다운이나 keepAlive 기능을 사용하지 않기 때문에 이것은 단지 제거 된 버전 이었지만 idleCutOffMoment를 사용하여 나의 수정을 볼 수있을 것입니다
Brian F

3
@BrianF 감사합니다. 사용자 정의 버전을 사용하고 있으므로 중요하지 않을 수 있지만 공식 릴리스에 추가 할 예정입니다.
moribvndvs

1
@HackedByChinese 감사합니다 :) 귀하의 스 니펫에서 이것이 작동하도록하기 위해해야 ​​할 전부입니까? 내가 추가 할 때까지 나를 위해 작동하지 않았다 Idle.watch().run()함수 IdleTimeout내가 그랬어 때까지 이벤트가 전혀 발사되지 않았다. 나는 Idle.watch()당신의 github 데모 에서 호출을 보았 으므로 그것이 내가 얻은 곳입니다.
Boris

24

사용중인 데모 보기 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>

아래 코드는 순수한 자바 스크립트 버전입니다.

<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에서 업데이트 된 버전을 보려면 여기를 클릭하십시오.


2
멋진 솔루션은 이벤트 이름을 반복하여 더 짧게 만들 수 있습니다. var eventNames = [ 'keydown', 'keyup', 'click', 'mousemove', 'DOMMouseScroll', 'mousewheel', 'mousedown', 'touchstart' , 'touchmove', 'scroll', 'focus']; for (var i = 0; i <eventNames.length; i ++) {bodyElement.bind (eventNames [i], TimeOut_Resetter); }

@Tom 귀하의 제안에 감사 드리며 귀하의 제안으로 제 답변을 업데이트했습니다.
Frank Myat Thu

나는 이것이 오래된 대답이라는 것을 알고 있지만 이것을 읽는 사람에게는 로그 아웃 섹션에서 이벤트를 꺼야합니다. 현재 (2017 년 12 월) bind는 더 이상 사용되지 않으므로 실행 bodyElement.on(...)하고 내부에서 LogoutByTimer실행 해야합니다bodyElement.off(...)
GChamon

이것은 꽤 멋지다. 사용자에게 onlick에 대한 경고를주기 위해 $ uib.modal을 추가했습니다.
Fergus

내 프로젝트 세부 사항에 맞게 솔루션을 약간 수정했지만 작동합니다! 감사합니다!
mp_loki

19

이를 수행하는 방법은 여러 가지가 있어야하며 각 접근 방식은 특정 애플리케이션에 더 잘 맞아야합니다. 대부분의 앱에서는 단순히 키 또는 마우스 이벤트를 처리하고 로그 아웃 타이머를 적절하게 활성화 / 비활성화 할 수 있습니다. 즉, 내 머리 위에 "멋진"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;
  });
});

4
나는 이것이 매우 새로운 해결책이라고 생각하고 꽤 많이 가지고 놀았습니다. 내가 가진 주요 문제는 이것이 사용자 주도가 아닌 다른 많은 이벤트 ($ intervals, $ timeouts 등)에서 실행되며 이러한 이벤트가 lastDigestRun을 재설정한다는 것입니다.
Seth M.

1
이 방법을 사용할 수 있으며 모든 다이제스트 대신 ngIdle 모듈이 아래에서 수행하는 것처럼 특정 이벤트를 확인하십시오. ie $ document.find ( 'body'). on ( 'mousemove keydown DOMMouseScroll mousewheel mousedown touchstart', checkIdleTimeout);
Brian F

다이제스트 시계의 아이디어가 마음에 들지만 웹 소켓이나 실행중인 무언가가 있으면 항상이를 사용하여 활동할 수 있습니다. 포인트를 무찌르는 힌트.
amcdnl

@BrianF Mmh는 Brian이 말한 것처럼 webSockets가 "mousemove", "keydown", "DOM-xxx"와 같은 이벤트를 트리거합니다. 나는 일반적으로 이것은 사실이 아닐 것입니다.
freeeman

3
"if (now-lastDigestRun> 10 * 60 * 60) {"은 1 분을 기다려야한다는 의미입니까?
Tomasz Waszczyk

11

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

1
좋은 expample. 3 행 "var lastRun = Date.now ();" 난 당신이 "lastDigestRun"로 변수를 의미 생각
마틴

실험을 위해 1 분 동안 활동이 없으면 시간 초과로 변경했습니다. 그리고 사용자가 활동하는 동안 시간 초과를 유지합니다. 무엇을 제공합니까?
BHR

사용 $rootScope.$watch에 전환을 요구,이 경우 setInterval이후, $intervalA는 각 함수 호출에 소화 트리거 효과적으로를 재설정 lastDigestRun.
alalonde

@ v-tec 내 응용 프로그램에서 귀하의 접근 방식을 사용하고 있지만 clearInterval을 사용하여 멈출 수없는 간격을 어떻게 지울 수 있습니까
Mohamed Sahir

6

angular-activity-monitor여러 공급자를 주입하는 것보다 더 간단한 방법으로 사용할 수도 있으며 , 다이제스트 루프를 수동으로 트리거하는 것을 방지하기 위해 setInterval()(vs. angular 's $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
  });
}

여러 질문에 동일한 답변을 게시하지 마십시오. 하나의 좋은 답변을 게시 한 다음 투표 / 플래그하여 다른 질문을 중복으로 종료합니다. 질문이 중복되지 않는 경우 질문에 대한 답변을 수정합니다 .
Martijn Pieters

이 솔루션을 구현하고 있지만 매우 멋지지만 다른 브라우저 활동도 추적하고 있습니까? 내 응용 프로그램을 제한하고 싶습니다. 사용자가 내 응용 프로그램에서 유휴 상태 인 다음 자동 로그 아웃 만 가능합니다.
user1532976

@ user1532976 : 웹 응용 프로그램에서 스크립트,이 글은 그래서 더 창 (탭)의 탈옥하지 않습니다 그것은 다른 활동을 추적하지 않습니다.
DDW

3

나는 Buu의 접근 방식을 시도했지만 $ interval 및 $ timeout 함수 실행을 포함하여 다이 제스터를 실행하도록 트리거하는 이벤트의 수가 너무 많기 때문에 제대로 얻을 수 없었습니다. 이렇게하면 사용자 입력에 관계없이 응용 프로그램이 유휴 상태가되지 않는 상태가됩니다.

실제로 사용자 유휴 시간을 추적해야하는 경우 좋은 각도 접근 방식이 있는지 확실하지 않습니다. https://github.com/witoldsz/angular-http-auth 여기에 Witoldz가 더 나은 접근 방식을 표시한다고 제안합니다 . 이 접근 방식은 자격 증명이 필요한 작업을 수행 할 때 사용자에게 재 인증하라는 메시지를 표시합니다. 사용자가 인증되면 이전에 실패한 요청이 다시 처리되고 아무 일도 일어나지 않은 것처럼 응용 프로그램이 계속됩니다.

이는 사용자의 인증이 만료 되더라도 여전히 애플리케이션 상태를 유지할 수 있고 작업을 잃지 않을 수 있기 때문에 사용자가 활성 상태 인 동안 사용자의 세션이 만료 될 수 있다는 우려를 처리합니다.

클라이언트에 일종의 세션 (쿠키, 토큰 등)이있는 경우 해당 세션도보고 만료되면 로그 아웃 프로세스를 트리거 할 수 있습니다.

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

업데이트 : 여기에 우려를 보여주는 플렁크가 있습니다. http://plnkr.co/edit/ELotD8W8VAeQfbYFin1W . 이것이 증명하는 것은 다이 제스터 실행 시간이 간격이 틱할 때만 업데이트된다는 것입니다. 간격이 최대 개수에 도달하면 소화기가 더 이상 실행되지 않습니다.


3

ng-Idle은 갈 길처럼 보이지만 Brian 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
        }
    }
});

1

Buu의 다이제스트 사이클 시계는 천재라고 생각합니다. 공유해 주셔서 감사합니다. 다른 사람들이 언급했듯이 $ 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();
    });
});

"10 * 60 * 1000"은 밀리 초 수입니까?
Tomasz Waszczyk

성능보기에서 어떤 방법이 더 잘 작동할까요 ?? 마지막 다이제스트보기 또는 이벤트보기 ???
Ravi Shanker Reddy

1

나는 이것을 위해 ng-idle을 사용하고 약간의 로그 아웃과 토큰 null 코드를 추가했으며 정상적으로 작동합니다. 이것을 시도해 볼 수 있습니다. 멋진 모듈을 만들어 주신 @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

더 큰 프로젝트에서 이것을 사용하는 사람에 대한 답변을 확장하고 싶습니다. 실수로 여러 이벤트 핸들러를 연결할 수 있으며 프로그램이 이상하게 작동합니다.

이를 없애기 위해 저는 공장에서 노출 된 싱글 톤 함수를 사용했습니다.이 함수에서 호출 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();
    }
  };

}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.