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

注册