2020年11月18日 星期三

[研究][JavaScript] 用 idle-timer.js 做 Session Time Out 前 N 秒自動彈出倒數計時對話盒視窗

[研究][JavaScript] 用 idle-timer.js ( jQuery Idle Timer Plugin v1.1.1  2020-06-25 版) 做 Session Time Out 前 N 秒自動彈出倒數計時對話盒視窗

2020-11-17

環境 Visual Studio 2019 v16.8.1 版 + C# + ASP.NET  + WebForm 

NuGet 要安裝數個套件,jQuery, BootStrap, popper, moment,不確定是否可缺。

敝人測試用 BootStrap 3.5.1 或 4.x 都可以用。

jQuery Idle Timer Plugin 要手動下載,把 idle-timer.js 拿過來用 ( 一個檔案就好)

https://github.com/thorst/jquery-idletimer


Download

Compressed ~3kb

https://raw.github.com/thorst/jquery-idletimer/master/dist/idle-timer.min.js

Uncompressed ~11kb

https://raw.github.com/thorst/jquery-idletimer/master/dist/idle-timer.js

不要下載 src 目錄下的 idle-timer.js,那個不是。


根據下面網址,jQuery Idle Timer Plugin 目前為 v1.1.1 2020-06-25 版

https://raw.githubusercontent.com/thorst/jquery-idletimer/master/dist/idle-timer.min.js


Default.aspx


<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication9.Default" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title></title>
    <%--相依性可能影響順序先後,但這個順序可用--%>
    <script src="Scripts/jquery-3.5.1.js"></script>
    <script src="Scripts/idle-timer.js"></script>
    <link href="Content/bootstrap.css" rel="stylesheet" />
    <script src="Scripts/popper.js"></script>
    <script src="Scripts/bootstrap.js"></script>
    <script src="Scripts/moment.js"></script>
</head>
<body>
    <form id="form1" runat="server">
        
        <div class="container">
            <h2>Concept</h2>
            <p>
                Wait 10 seconds, you will see a expiring warning. Wait 10 more seconds and you will see that you have been logged out.
       
            </p>
            
        </div>
        <div class="modal fade" id="mdlExpirationWarning" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true" data-backdrop="static" data-keyboard="false">
            <div class="modal-dialog" role="document">
                <div class="modal-content">
                    <div class="modal-header">
                        <h5 class="modal-title" id="exampleModalLabel">Session Expiration Warning</h5>

                    </div>
                    <div class="modal-body">
                        <p>You've been inactive for a while. For your security, we'll log you out automatically. Click "Stay Online" to continue your session. </p>
                        <p>
                            Your session will expire in <span class="bold" id="sessionSecondsRemaining">120</span> seconds.
                   
                        </p>
                    </div>
                    <div class="modal-footer">
                        <button id="extendSession" type="button" class="btn btn-primary" data-dismiss="modal">
                            Stay
                        Online</button>
                        <button id="logoutSession" type="button" class="btn btn-secondary">Logout</button>
                    </div>
                </div>
            </div>
        </div>

        <div class="modal fade" id="mdlLoggedOut" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true" data-backdrop="static" data-keyboard="false">
            <div class="modal-dialog" role="document">
                <div class="modal-content">
                    <div class="modal-header">
                        <h5 class="modal-title" id="exampleModalLabel">You have been logged out</h5>
                    </div>
                    <div class="modal-body">
                        <p>Your session has expired.</p>
                    </div>
                </div>
            </div>
        </div>
    </form>

    <script>
        // You could pull this out to its own file very easily
        window.app = window.app || {};

        app.session = {

            //Settings
            warningTimeout: 10000, //(ms) The time we give them to say they want to stay signed in
            inactiveTimeout: 20000, //(ms) The time until we display a warning message
            minWarning: 5000, //(ms) If they come back to page (on mobile), The minumum amount, before we just log them out
            timerSyncId: "SomethingUnique", //The key idleTimer will use to write to localStorage
            logoutUrl: "/logout", //Your url to log out, if you want you could build the url to pass a referal param
            keepAliveUrl: "api/user/KeepAlive", // The url for the keepalive api
            keepaliveInterval: 5000, //(ms) the interval to call keep alive url
            //From here down you shouldnt have to alter anything
            warningStart: null, //Date time the warning was started
            warningTimer: null, //Timer running every second to countdown to logout
            keepaliveTimer: null, //Timer for independent ping to keep session alive
            logout: function () {
                //Write to storage to tell other tab its time to sign out
                if (typeof (localStorage) !== "undefined") {
                    localStorage.setItem(app.session.timerSyncId, 0);
                }

                //Send this page to the logout url, that will destroy session and forward to login
                //window.location = app.session.logoutUrl;

                //To simulate logout we are just showing the logout dialog and locking the screen
                $("#mdlExpirationWarning").modal("hide");
                $("#mdlLoggedOut").modal("show");
            },
            keepAlive: function () {
                //Hide logout modal
                $("#mdlExpirationWarning").modal("hide");

                //Clear the timer
                clearTimeout(app.session.warningTimer);
                app.session.warningTimer = null;

                //Restart the idleTimer
                $(document).idleTimer("reset");
            },
            startKeepAliveTimer: function () {
                // Basically I just poll the server half way through the session life
                // to make sure the servers session stays valid
                clearTimeout(app.session.keepaliveTimer);
                app.session.keepaliveTimer = setInterval(function () {
                    app.session.sendKeepAlive();
                }, (app.session.inactiveTimeout / 2));
            },
            sendKeepAlive: function () {
                // Write a new date to storage so any other tabs are informed that this tab
                //  sent the keepalive
                if (typeof (localStorage) !== "undefined") {
                    localStorage.setItem(app.session.timerSyncId + "_keepalive", +new Date());
                }

                // The actual call to the keep alive api
                //$.post(app.session.keepAliveUrl).fail(function (jqXHR) {
                //    if (jqXHR.status == 500 || jqXHR.status == 0) {
                //        app.session.logout();
                //    }
                //});
            },
            showWarning: function (obj) {
                //Get time when user was last active
                var diff = (+new Date()) - obj.lastActive - obj.timeout,
                    warning = (+new Date()) - diff;

                // Destroy idleTimer so users are forced to click the extend button
                $(document).idleTimer("pause");

                //On mobile js is paused, so see if this was triggered while we were sleeping
                if (diff >= app.session.warningTimeout || warning <= app.session.minWarning) {
                    app.session.logout();
                } else {

                    //Show dialog, and note the time
                    $('#sessionSecondsRemaining').html(Math.round((app.session.warningTimeout - diff) / 1000));
                    $("#mdlExpirationWarning").modal("show");
                    app.session.warningStart = (+new Date()) - diff;

                    //Update counter downer every second
                    app.session.warningTimer = setInterval(function () {
                        var remaining = Math.round((app.session.warningTimeout / 1000) - (((+new Date()) - app.session.warningStart) / 1000));

                        if (remaining >= 0) {
                            $('#sessionSecondsRemaining').html(remaining);
                        } else {
                            app.session.logout();
                        }
                    }, 1000)
                }
            },
            localWrite: function (e) {

                if (typeof (localStorage) !== "undefined" && e.originalEvent.key == app.session.timerSyncId && app.session.warningTimer != null) {
                    // If another tab has written to cache then
                    if (e.originalEvent.newValue == 0) {
                        // If they wrote a 0 that means they chose to logout when prompted
                        app.session.logout();
                    } else {
                        // They chose to stay online, so hide the dialog
                        app.session.keepAlive();
                    }

                } else if (typeof (localStorage) !== "undefined" && e.originalEvent.key == app.session.timerSyncId + "_keepalive") {
                    // If the other tab sent a keepAlive poll to the server, reset the time here so we dont send two updates
                    // This isnt really needed per se but it will save some server load
                    app.session.startKeepAliveTimer();
                }
            }
        };

        $(function () {
            //This will fire at X after page load, to show an inactive warning
            $(document).on("idle.idleTimer", function (event, elem, obj) {
                app.session.showWarning(obj);
            });

            //Create a timer to keep server session alive, independent of idleTimer
            app.session.startKeepAliveTimer();

            //User clicked ok to extend session
            $("#extendSession").click(function () {
                app.session.keepAlive(); //Remove the warning dialog etc
            });

            //User clicked logout
            $("#logoutSession").click(function () {
                app.session.logout();
            });

            //Set up the idleTimer, if inactive for X seconds log them out
            $(document).idleTimer({
                timeout: app.session.inactiveTimeout - app.session.warningTimeout,
                timerSyncId: app.session.timerSyncId
            });

            // Monitor writes by other tabs
            $(window).bind("storage", app.session.localWrite);
        });
    </script>
</body>
</html>



Default.aspx.cs  (不用特別去修改)


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace WebApplication1
{
    public partial class Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {

        }
    }
}

Web.Config (預設 20 分鐘,可以不用特別去設定)

<configuration>
  <system.web>
    <sessionState timeout="20"></sessionState>
  </system.web>
</configuration>

(下圖)執行結果,等10秒就會出現



「Stay Online」按鈕按下去,會再多 10 秒可用。
按下 Logout 或不按,最後會跳出

Stay Online 按鈕 (斷成2行) 和訊息文字 是寫在 .aspx 中,而不是 idle-timer.js 中,訊息換成中文字應該沒問題。

如果真的要按鈕後轉址,要再手動修改 logoutUrl 的值,以及相關程式 (預設註解掉)。

//window.location = app.session.logoutUrl;
改成
window.location = app.session.logoutUrl;
實際測試,對話盒視窗跳出後不動它,確實會自動轉址到 logoutUrl 指定的網址。
但是從按下按鈕到轉址發生,需要數秒鐘,有些慢。

keepAliveUrl 實際測試好像不會轉址,或我不會用。

********************************************************************************
2020-11-19 套用到某個系統後,結果不能用,不會跳出對話盒視窗,也沒錯誤訊息。
在 Visual Studio 2019 環境執行,會出現

行: 4055
錯誤: 物件沒有支援這個屬性或方法 'idleTimer'



<script src="Scripts/moment.js"></script>
之後再加上一行 (等於 jQuery 載入2次)
<script src="Scripts/jquery-3.5.1.js"></script>
測試結果錯誤訊息相同為
錯誤: 物件沒有支援這個屬性或方法 'idleTimer'

(完)

相關

[研究][JavaScript] 用 timeout-dialog.js 做 Session Time Out 前 60 秒自動彈出倒數計時對話盒視窗
http://shaurong.blogspot.com/2020/11/javascript-timeout-dialogjs-session.html

[研究][C#][ASP.NET][WebForm] Sessionn Time Out 自動登出前倒數計時
http://shaurong.blogspot.com/2020/11/caspnetwebform-sessionn-time-out.html

[研究][C#][ASP.NET][WebForm] Master Page 的 Sessionn Time Out 自動登出前倒數計時
http://shaurong.blogspot.com/2020/11/caspnetwebform-master-page-sessionn.html


沒有留言:

張貼留言