为 durandal 单页应用程序创建会话超时警告
Create session timeout warning for durandal single page application
我有一个 durandal/requirejs 单页应用程序。当用户闲置时,我需要向用户显示一条警告,表明会话即将超时。我在互联网上查看了 asp.net 应用程序的几个示例,但找不到任何单页应用程序的示例。
我的应用程序类似于 John Papa 的代码露营者(MVC 应用程序)。
如果用户的会话距离超时还有 2 分钟,我如何才能向用户发出会话超时警告?
--编辑
在我的 main.js 文件中我有-
app.setRoot('viewmodels/shell', 'entrance');
router.guardRoute = function (instance, instruction) {
var sess_pollInterval = 60000;
//How many minutes the session is valid for
var sess_expirationMinutes = 2;
//How many minutes before the warning prompt
var sess_warningMinutes = 1;
var sess_intervalID;
var sess_lastActivity;
initSessionMonitor();
function initSessionMonitor() {
sess_lastActivity = new Date();
sessSetInterval();
$(document).bind('keypress.session', function (ed, e) { sessKeyPressed(ed, e); });
}
function sessSetInterval() {
sess_intervalID = setInterval('sessInterval()', sess_pollInterval);
}
function sessClearInterval() {
clearInterval(sess_intervalID);
}
function sessKeyPressed(ed, e) {
sess_lastActivity = new Date();
}
function sessPingServer() {
//Call an AJAX function to keep-alive your session.
alert('someAJAXFunction();');
}
function sessLogOut() {
alert('here');
//window.location.href = '/Account/LogOff';
}
function sessInterval() {
var now = new Date();
var diff = now - sess_lastActivity;
var diffMins = (diff / 1000 / 60);
if (diffMins >= sess_warningMinutes) {
//wran before expiring
//stop the timer
sessClearInterval();
//promt for attention
if (confirm('Your session will expire in ' + (sess_expirationMinutes - sess_warningMinutes) +
' minutes (as of ' + now.toTimeString() + '), press OK to remain logged in ' +
'or press Cancel to log off. \nIf you are logged off any changes will be lost.')) {
now = new Date();
diff = now - sess_lastActivity;
diffMins = (diff / 1000 / 60);
if (diffMins > sess_expirationMinutes) {
//timed out
sessLogOut();
}
else {
//reset inactivity timer
sessPingServer();
sessSetInterval();
sess_lastActivity = new Date();
}
} else {
sessLogOut();
}
} else {
sessPingServer();
}
}
return true;
};
}
现在有 "Uncaught ReferenceError: sessInterval is not defined." 个想法?
以下是我在 idle
服务中的做法。它使用一些其他服务,但您应该明白这一点。基本上,我开始在可观察的用户 activity 唱歌时开始跟踪他,并在每次可观察的变化时重置空闲处理程序的超时。
//idle.js
define(function (require) {
var ko = require('knockout'),
$ = require('jquery'),
router = require('lib/router'),
config = require('lib/config'),
dialog = require('lib/dialog'),
auth = require('auth/auth'),
lastActionDate = ko.observable(),
signoutHandle = null,
onIdle = function () {
console.log('user has been idle, signing out');
return auth.signOut()
.then(function () {
router.navigate('');
dialog.show('auth/idle');
});
},
init = function () {
var userActionHandler = function () {
lastActionDate(new Date());
};
auth.on('signin:success').then(function (user) {
$(document).on('click keydown scroll', userActionHandler);
userActionHandler();
});
auth.on('signout:success').then(function (using) {
$(document).off('click keydown scroll', userActionHandler);
});
lastActionDate.subscribe(function () {
if (signoutHandle) {
clearTimeout(signoutHandle);
}
signoutHandle = setTimeout(onIdle, config.get('idleTimeout') * 1000);
});
};
return {
init: init
};
});
然后我只需在 app.start()
之前调用 idle.init() 我的 main.js 文件
我使用的方法与上面的 post 不同。我使用了 timeout-dialog.js 并更改了该脚本以与 durandal 的路由器和我的应用程序中需要的任何其他服务一起使用。我也用过idle js。这是代码-
main.js 在 app.start()-
var timeout = 100;
$(document).bind("idle.idleTimer", function () {
controls.timeoutDialog.setupDialogTimer();
});
$(document).bind("active.idleTimer", function () {
var sess = Security.GetKeepSessionAlive();
});
$.idleTimer(timeout);
超时-dialog.js代码-
String.prototype.format = function () {
var s = this,
i = arguments.length;
while (i--) {
s = s.replace(new RegExp('\{' + i + '\}', 'gm'), arguments[i]);
}
return s;
};
define(['durandal/system', 'plugins/router', 'services/logger', 'services/SecurityDataService'],
function (system, router, logger, Security){
timeoutDialog = {
settings: {
timeout: 50,
countdown: 15,
title: 'Your session is about to expire!',
message: 'You will be logged out in {0} seconds.',
question: 'Do you want to stay signed in?',
keep_alive_button_text: 'Yes, Keep me signed in',
sign_out_button_text: 'No, Sign me out',
keep_alive_url: '',
keep_alive_function: function () {
},
logout_url: function () {
router.map([
{ route: 'ErrorPage', moduleId: 'ErrorPage', title: 'ErrorPage', title: 'ErrorPage', nav: false }
]).activate
router.navigate('ErrorPage');
},
logout_redirect_url: function () {
router.map([
{ route: 'ErrorPage', moduleId: 'ErrorPage', title: 'ErrorPage', title: 'ErrorPage', nav: false }
]).activate
router.navigate('ErrorPage');
},
logout_function: function () {
amplify.store("ErrorDetails", "Session Timed Out!");
router.map([
{ route: 'ErrorPage', moduleId: 'ErrorPage', title: 'ErrorPage', title: 'ErrorPage', nav: false }
]).activate
router.navigate('ErrorPage');
},
restart_on_yes: true,
dialog_width: 350
},
alertSetTimeoutHandle: 0,
setupDialogTimer: function (options) {
if (options !== undefined) {
$.extend(this.settings, options);
}
var self = this;
if (self.alertSetTimeoutHandle !== 0) {
clearTimeout(self.alertSetTimeoutHandle);
}
self.alertSetTimeoutHandle = window.setTimeout(function () {
self.setupDialog();
}, (this.settings.timeout - this.settings.countdown) * 1000);
},
setupDialog: function () {
//check for other modal forms on view
//$.element.modal('hide');
$('.modal').modal('hide');
var self = this;
self.destroyDialog();
$('<div id="timeout-dialog">' +
'<p id="timeout-message">' + this.settings.message.format('<span id="timeout-countdown">' + this.settings.countdown + '</span>') + '</p>' +
'<p id="timeout-question">' + this.settings.question + '</p>' +
'</div>')
.appendTo('body')
.dialog({
modal: true,
width: this.settings.dialog_width,
minHeight: 'auto',
zIndex: 10000,
closeOnEscape: false,
draggable: false,
resizable: false,
dialogClass: 'timeout-dialog',
title: this.settings.title,
buttons: {
'keep-alive-button': {
text: this.settings.keep_alive_button_text,
id: "timeout-keep-signin-btn",
click: function () {
self.keepAlive();
}
},
'sign-out-button': {
text: this.settings.sign_out_button_text,
id: "timeout-sign-out-button",
click: function () {
self.signOut(true);
}
}
}
});
self.startCountdown();
},
destroyDialog: function () {
if ($("#timeout-dialog").length) {
$("#timeout-dialog").dialog("close");
$('#timeout-dialog').remove();
}
},
startCountdown: function () {
var self = this,
counter = this.settings.countdown;
this.countdown = window.setInterval(function () {
counter -= 1;
$("#timeout-countdown").html(counter);
if (counter <= 0) {
window.clearInterval(self.countdown);
self.signOut(false);
}
}, 1000);
},
keepAlive: function () {
var self = this;
this.destroyDialog();
window.clearInterval(this.countdown);
this.settings.keep_alive_function();
if (this.settings.keep_alive_url !== '') {
$.get(this.settings.keep_alive_url, function (data) {
if (data === "OK") {
if (this.settings.restart_on_yes) {
self.setupDialogTimer();
}
}
else {
self.signOut(false);
}
});
}
},
signOut: function (is_forced) {
var self = this;
this.destroyDialog();
this.settings.logout_function(is_forced);
if (this.settings.logout_url !== null) {
$.post(this.settings.logout_url, function (data) {
self.redirectLogout(is_forced);
});
}
else {
self.redirectLogout(is_forced);
}
},
redirectLogout: function (is_forced) {
var target = this.settings.logout_redirect_url + '?next=' + encodeURIComponent(window.location.pathname + window.location.search);
if (!is_forced)
target += '&timeout=t';
window.location = target;
},
};
var dataservice = {
timeoutDialog: timeoutDialog
};
return dataservice;
});
我将 timeout-dialog.js 放在我自己的应用程序文件夹下的文件夹中,以引入我需要的 durandal 和其他服务。 idle-timer.js 留在脚本文件夹中并通过 bundle.config.
注册
我有一个 durandal/requirejs 单页应用程序。当用户闲置时,我需要向用户显示一条警告,表明会话即将超时。我在互联网上查看了 asp.net 应用程序的几个示例,但找不到任何单页应用程序的示例。
我的应用程序类似于 John Papa 的代码露营者(MVC 应用程序)。
如果用户的会话距离超时还有 2 分钟,我如何才能向用户发出会话超时警告?
--编辑 在我的 main.js 文件中我有-
app.setRoot('viewmodels/shell', 'entrance');
router.guardRoute = function (instance, instruction) {
var sess_pollInterval = 60000;
//How many minutes the session is valid for
var sess_expirationMinutes = 2;
//How many minutes before the warning prompt
var sess_warningMinutes = 1;
var sess_intervalID;
var sess_lastActivity;
initSessionMonitor();
function initSessionMonitor() {
sess_lastActivity = new Date();
sessSetInterval();
$(document).bind('keypress.session', function (ed, e) { sessKeyPressed(ed, e); });
}
function sessSetInterval() {
sess_intervalID = setInterval('sessInterval()', sess_pollInterval);
}
function sessClearInterval() {
clearInterval(sess_intervalID);
}
function sessKeyPressed(ed, e) {
sess_lastActivity = new Date();
}
function sessPingServer() {
//Call an AJAX function to keep-alive your session.
alert('someAJAXFunction();');
}
function sessLogOut() {
alert('here');
//window.location.href = '/Account/LogOff';
}
function sessInterval() {
var now = new Date();
var diff = now - sess_lastActivity;
var diffMins = (diff / 1000 / 60);
if (diffMins >= sess_warningMinutes) {
//wran before expiring
//stop the timer
sessClearInterval();
//promt for attention
if (confirm('Your session will expire in ' + (sess_expirationMinutes - sess_warningMinutes) +
' minutes (as of ' + now.toTimeString() + '), press OK to remain logged in ' +
'or press Cancel to log off. \nIf you are logged off any changes will be lost.')) {
now = new Date();
diff = now - sess_lastActivity;
diffMins = (diff / 1000 / 60);
if (diffMins > sess_expirationMinutes) {
//timed out
sessLogOut();
}
else {
//reset inactivity timer
sessPingServer();
sessSetInterval();
sess_lastActivity = new Date();
}
} else {
sessLogOut();
}
} else {
sessPingServer();
}
}
return true;
};
}
现在有 "Uncaught ReferenceError: sessInterval is not defined." 个想法?
以下是我在 idle
服务中的做法。它使用一些其他服务,但您应该明白这一点。基本上,我开始在可观察的用户 activity 唱歌时开始跟踪他,并在每次可观察的变化时重置空闲处理程序的超时。
//idle.js
define(function (require) {
var ko = require('knockout'),
$ = require('jquery'),
router = require('lib/router'),
config = require('lib/config'),
dialog = require('lib/dialog'),
auth = require('auth/auth'),
lastActionDate = ko.observable(),
signoutHandle = null,
onIdle = function () {
console.log('user has been idle, signing out');
return auth.signOut()
.then(function () {
router.navigate('');
dialog.show('auth/idle');
});
},
init = function () {
var userActionHandler = function () {
lastActionDate(new Date());
};
auth.on('signin:success').then(function (user) {
$(document).on('click keydown scroll', userActionHandler);
userActionHandler();
});
auth.on('signout:success').then(function (using) {
$(document).off('click keydown scroll', userActionHandler);
});
lastActionDate.subscribe(function () {
if (signoutHandle) {
clearTimeout(signoutHandle);
}
signoutHandle = setTimeout(onIdle, config.get('idleTimeout') * 1000);
});
};
return {
init: init
};
});
然后我只需在 app.start()
之前调用 idle.init() 我的 main.js 文件我使用的方法与上面的 post 不同。我使用了 timeout-dialog.js 并更改了该脚本以与 durandal 的路由器和我的应用程序中需要的任何其他服务一起使用。我也用过idle js。这是代码-
main.js 在 app.start()-
var timeout = 100;
$(document).bind("idle.idleTimer", function () {
controls.timeoutDialog.setupDialogTimer();
});
$(document).bind("active.idleTimer", function () {
var sess = Security.GetKeepSessionAlive();
});
$.idleTimer(timeout);
超时-dialog.js代码-
String.prototype.format = function () {
var s = this,
i = arguments.length;
while (i--) {
s = s.replace(new RegExp('\{' + i + '\}', 'gm'), arguments[i]);
}
return s;
};
define(['durandal/system', 'plugins/router', 'services/logger', 'services/SecurityDataService'],
function (system, router, logger, Security){
timeoutDialog = {
settings: {
timeout: 50,
countdown: 15,
title: 'Your session is about to expire!',
message: 'You will be logged out in {0} seconds.',
question: 'Do you want to stay signed in?',
keep_alive_button_text: 'Yes, Keep me signed in',
sign_out_button_text: 'No, Sign me out',
keep_alive_url: '',
keep_alive_function: function () {
},
logout_url: function () {
router.map([
{ route: 'ErrorPage', moduleId: 'ErrorPage', title: 'ErrorPage', title: 'ErrorPage', nav: false }
]).activate
router.navigate('ErrorPage');
},
logout_redirect_url: function () {
router.map([
{ route: 'ErrorPage', moduleId: 'ErrorPage', title: 'ErrorPage', title: 'ErrorPage', nav: false }
]).activate
router.navigate('ErrorPage');
},
logout_function: function () {
amplify.store("ErrorDetails", "Session Timed Out!");
router.map([
{ route: 'ErrorPage', moduleId: 'ErrorPage', title: 'ErrorPage', title: 'ErrorPage', nav: false }
]).activate
router.navigate('ErrorPage');
},
restart_on_yes: true,
dialog_width: 350
},
alertSetTimeoutHandle: 0,
setupDialogTimer: function (options) {
if (options !== undefined) {
$.extend(this.settings, options);
}
var self = this;
if (self.alertSetTimeoutHandle !== 0) {
clearTimeout(self.alertSetTimeoutHandle);
}
self.alertSetTimeoutHandle = window.setTimeout(function () {
self.setupDialog();
}, (this.settings.timeout - this.settings.countdown) * 1000);
},
setupDialog: function () {
//check for other modal forms on view
//$.element.modal('hide');
$('.modal').modal('hide');
var self = this;
self.destroyDialog();
$('<div id="timeout-dialog">' +
'<p id="timeout-message">' + this.settings.message.format('<span id="timeout-countdown">' + this.settings.countdown + '</span>') + '</p>' +
'<p id="timeout-question">' + this.settings.question + '</p>' +
'</div>')
.appendTo('body')
.dialog({
modal: true,
width: this.settings.dialog_width,
minHeight: 'auto',
zIndex: 10000,
closeOnEscape: false,
draggable: false,
resizable: false,
dialogClass: 'timeout-dialog',
title: this.settings.title,
buttons: {
'keep-alive-button': {
text: this.settings.keep_alive_button_text,
id: "timeout-keep-signin-btn",
click: function () {
self.keepAlive();
}
},
'sign-out-button': {
text: this.settings.sign_out_button_text,
id: "timeout-sign-out-button",
click: function () {
self.signOut(true);
}
}
}
});
self.startCountdown();
},
destroyDialog: function () {
if ($("#timeout-dialog").length) {
$("#timeout-dialog").dialog("close");
$('#timeout-dialog').remove();
}
},
startCountdown: function () {
var self = this,
counter = this.settings.countdown;
this.countdown = window.setInterval(function () {
counter -= 1;
$("#timeout-countdown").html(counter);
if (counter <= 0) {
window.clearInterval(self.countdown);
self.signOut(false);
}
}, 1000);
},
keepAlive: function () {
var self = this;
this.destroyDialog();
window.clearInterval(this.countdown);
this.settings.keep_alive_function();
if (this.settings.keep_alive_url !== '') {
$.get(this.settings.keep_alive_url, function (data) {
if (data === "OK") {
if (this.settings.restart_on_yes) {
self.setupDialogTimer();
}
}
else {
self.signOut(false);
}
});
}
},
signOut: function (is_forced) {
var self = this;
this.destroyDialog();
this.settings.logout_function(is_forced);
if (this.settings.logout_url !== null) {
$.post(this.settings.logout_url, function (data) {
self.redirectLogout(is_forced);
});
}
else {
self.redirectLogout(is_forced);
}
},
redirectLogout: function (is_forced) {
var target = this.settings.logout_redirect_url + '?next=' + encodeURIComponent(window.location.pathname + window.location.search);
if (!is_forced)
target += '&timeout=t';
window.location = target;
},
};
var dataservice = {
timeoutDialog: timeoutDialog
};
return dataservice;
});
我将 timeout-dialog.js 放在我自己的应用程序文件夹下的文件夹中,以引入我需要的 durandal 和其他服务。 idle-timer.js 留在脚本文件夹中并通过 bundle.config.
注册