angularjs - cloak - ng-init
Cierre de sesión automático con Angularjs basado en usuario inactivo (10)
Creo que el reloj del ciclo de digestión de Buu es genio. Gracias por compartir. Como otros han notado $ interval también hace que se ejecute el ciclo de resumen. Podríamos con el propósito de iniciar sesión automáticamente que el usuario use setInterval, lo que no causará un ciclo 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();
});
});
¿Es posible determinar si un usuario está inactivo y cerrar la sesión automáticamente después de decir 10 minutos de inactividad usando angularjs?
Estaba tratando de evitar el uso de jQuery, pero no puedo encontrar ningún tutorial o artículo sobre cómo hacer esto en angularjs. Cualquier ayuda sería apreciada.
Debería haber diferentes maneras de hacerlo y cada enfoque debería ajustarse mejor a una aplicación particular que a otra. Para la mayoría de las aplicaciones, simplemente puede manejar los eventos clave o del mouse y habilitar / deshabilitar un temporizador de cierre de sesión de manera adecuada. Dicho esto, en lo alto de mi cabeza, una solución "elegante" de AngularJS-y está monitoreando el ciclo de digestión, si no se ha activado ninguna durante la última [duración especificada] y luego se ha desconectado. Algo como esto.
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;
});
});
He usado ng-idle para esto y he añadido un pequeño código de cierre de sesión y token nulo y está funcionando bien, puedes probar esto. Gracias @HackedByChinese por hacer un módulo tan bueno.
En IdleTimeout, acabo de eliminar mis datos de sesión y token.
Aquí está mi código
$scope.$on(''IdleTimeout'', function () {
closeModals();
delete $window.sessionStorage.token;
$state.go("login");
$scope.timedout = $uibModal.open({
templateUrl: ''timedout-dialog.html'',
windowClass: ''modal-danger''
});
});
Jugado con el enfoque de Boo, sin embargo, no me gusta el hecho de que el usuario fue expulsado solo una vez que se ejecuta otro compendio, lo que significa que el usuario permanece conectado hasta que intenta hacer algo dentro de la página y luego se inicia inmediatamente.
Intento forzar el cierre de sesión mediante un intervalo que comprueba cada minuto si el último tiempo de acción fue hace más de 30 minutos. Lo enganché en $ routeChangeStart, pero también podría estar enganchado en $ rootScope. $ Watch como en el ejemplo de Boo.
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();
});
});
Me gustaría expandir las respuestas a quien quiera que esté usando esto en un proyecto más grande, podría adjuntar accidentalmente múltiples manejadores de eventos y el programa se comportaría de manera extraña.
Para deshacerme de eso, utilicé una función singleton expuesta por una fábrica, desde la que llamaría inactivityTimeoutFactory.switchTimeoutOn()
y inactivityTimeoutFactory.switchTimeoutOff()
en su aplicación angular para activar y desactivar respectivamente el cierre de sesión debido a la funcionalidad de inactividad.
De esta forma, se asegura de que solo ejecuta una instancia única de los controladores de eventos, sin importar cuántas veces intente activar el procedimiento de tiempo de espera, lo que facilita su uso en aplicaciones donde el usuario puede iniciar sesión desde diferentes rutas.
Aquí está mi código:
''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();
}
};
}
Probé el enfoque de Buu y no pude hacerlo del todo bien debido a la gran cantidad de eventos que activan la ejecución del digestor, incluidas las funciones $ interval y $ timeout que se ejecutan. Esto deja la aplicación en un estado en el que nunca estará inactiva independientemente de la entrada del usuario.
Si realmente necesita rastrear el tiempo de inactividad del usuario, no estoy seguro de que haya un buen enfoque angular. Sugeriría que Witoldz represente un mejor enfoque aquí https://github.com/witoldsz/angular-http-auth . Este enfoque pedirá al usuario que se vuelva a autenticar cuando se tome una acción que requiera sus credenciales. Una vez que el usuario ha autenticado, la solicitud fallida anterior se vuelve a procesar y la aplicación continúa como si nada hubiera sucedido.
Esto maneja la preocupación que podría tener de permitir que la sesión del usuario caduque mientras están activos, ya que incluso si su autenticación caduca, aún pueden conservar el estado de la aplicación y no perder ningún trabajo.
Si tiene algún tipo de sesión en su cliente (cookies, tokens, etc.) podría verlos también y activar su proceso de cierre de sesión si caducan.
app.run([''$interval'', function($interval) {
$interval(function() {
if (/* session still exists */) {
} else {
// log out of client
}
}, 1000);
}]);
ACTUALIZACIÓN: Aquí hay un plan que demuestra la preocupación. http://plnkr.co/edit/ELotD8W8VAeQfbYFin1W . Lo que esto demuestra es que el tiempo de ejecución del digestor se actualiza solo cuando el intervalo funciona. Una vez que el intervalo alcanza el conteo máximo, el digestor ya no se ejecutará.
También puedes lograr usar el angular-activity-monitor
una manera más directa que inyectando múltiples proveedores y usa setInterval()
(versus el $interval
de angular) para evitar activar manualmente un ciclo de resumen (que es importante para evitar que los elementos permanezcan vivos involuntariamente) )
En última instancia, se suscribe a algunos eventos que determinan cuándo un usuario está inactivo o se acerca. Por lo tanto, si desea cerrar la sesión de un usuario luego de 10 minutos de inactividad, puede usar el siguiente fragmento de código:
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
});
}
Vea la Demo que está usando angularjs
y vea el registro de su navegador
<!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>
El siguiente código es una versión pura de 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>
ACTUALIZAR
Actualicé mi solución como sugerencia de @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>
ng-Idle parece ser el camino a seguir, pero no pude entender las modificaciones de Brian F y también quería esperar para una sesión de descanso, también tenía un caso de uso bastante simple en mente. Lo reduje al código a continuación. Engancha eventos para restablecer un indicador de tiempo de espera (colocado de forma diferida en $ rootScope). Solo detecta el tiempo de espera que ha pasado cuando el usuario regresa (y desencadena un evento) pero eso es suficiente para mí. No pude conseguir que la ubicación de $ angulares funcionara aquí, pero de nuevo, el uso de document.location.href hace el trabajo.
Puse esto en mi app.js después de que se haya ejecutado .config.
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
}
}
});
Escribí un módulo llamado Ng-Idle
que puede ser útil para usted en esta situación. Aquí está la página que contiene instrucciones y una demostración.
Básicamente, tiene un servicio que inicia un temporizador para su duración inactiva que puede verse afectada por la actividad del usuario (eventos, como hacer clic, desplazarse, teclear). También puede interrumpir manualmente el tiempo de espera llamando a un método en el servicio. Si el tiempo de espera no se interrumpe, cuenta regresivamente una advertencia en la que podría alertar al usuario de que cerrarán la sesión. Si no responden después de que la cuenta regresiva de advertencia llega a 0, se emite un evento al que su aplicación puede responder. En su caso, podría emitir una solicitud para cancelar su sesión y redirigir a una página de inicio de sesión.
Además, tiene un servicio keep-alive que puede hacer ping a alguna URL en un intervalo. Esto puede ser utilizado por su aplicación para mantener viva la sesión de un usuario mientras están activos. El servicio inactivo se integra por defecto con el servicio keep-alive, suspendiendo el ping si se quedan inactivos y reanudándolo cuando regresan.
Toda la información que necesita para comenzar está en el sitio con más detalles en la wiki . Sin embargo, aquí hay un fragmento de configuración que muestra cómo cerrar sesión cuando finalizan el tiempo de espera.
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
});
});