實作網頁與後端Session Timeout
Timeout的核心部分引用Versatility Werks的程式碼jTimeout.js(程式碼貼於此文章最下方),Html頁面實作則分三部分
jTimeout.js的一些參數設定與功能實作
<script src="your_js_path/jTimeout.js"></script>
<script>
$(function(){
$.jTimeout({
/* 10分鐘Timeout */
'onClickExtend': function(jTimeout){
window.localStorage.timeoutCountdown = 600
$('#consTime').text( 600 );
},
/* 倒數60秒後不延長則Timeout,秒數設定在jTimeout的secondsPrior參數 */
'onPriorCallback': function(timeout, seconds){
timeout.options.extendOnMouseMove=false;
timeout.mouseMoved = true;
$("#timeoutModal").modal('show');
},
/* 滑鼠移動延長Session設定為False */
'extendOnMouseMove': false,
/* 時間到會觸發的url */
'onTimeout': function(timeout){
/* Force logout */
window.location.href="logout_url";
}
});
var timer,
setTimer = function(){
timer = window.setInterval(function(){
$('#curTime').val( window.localStorage.timeoutCountdown );
$('#consTime').text( window.localStorage.timeoutCountdown );
}, 1000);
};
setTimer();
});
</script>
需要根據環境客製化的有
- your_js_path
- logout_url
延伸Server的Session與頁面Timeout Reset
<script> function reset(){ $.jTimeout.reset(); var URL = "Server_Session_Extend_Url"; $.ajax({ type : 'POST', url: URL, success: function (resp) { $.jTimeout.reset(); } }); } </script>
需要根據環境客製化的有
- Server_Session_Extend_Url
頁面跳出的Modal倒數60秒後會自動登出,可點選延長Session或直接登出
<div class="modal fade" id="timeoutModal" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header" style="text-align: center;">
<h4 class="modal-title">閒置時間過久</h4>
</div>
<div class="modal-body" style="text-align: center;">
<label>距離系統強制登出剩餘</label> <label id='consTime'></label><label>秒</label>
</div>
<div class="modal-footer" style="text-align: center;">
<button type="button" class="btn btn-primary" data-dismiss="modal" onclick="reset();">
延長
</button>
<a href="logout_url">
登出
</a>
</div>
</div>
</div>
</div>
需要根據環境客製化的有
- logout_url
jTimeout.js
/*
*
*
jTimeout v2.1
Made with love by Versatility Werks (http://flwebsites.biz)
MIT Licensed
*
*
*/
(function ($) {
$.jTimeout = function (options) {
if( typeof window.jTimeout !== 'undefined' )
{
return window.jTimeout;
}
options = $.extend({}, $.jTimeout.defaults, options);
var timeout =
{
/**
* Whether or not the session has timed out
*/
timedOut: false,
/**
* Whether or not there is currently a timeout warning on the screen
*/
timeoutWarning: false,
/**
* The interval loop for counting down
*/
interval: false,
/**
* The settimeout for mouse movement to be debounced
*/
mouseTimeout: false,
options: options,
/**
* Generates a random ID to identify the current tab
*
* @param separator
* @returns {*}
*/
generateUid: function(separator)
{
var delim = separator || "-";
return (this.S4() + this.S4() + delim + this.S4() + delim + this.S4() + delim + this.S4() + delim + this.S4() + this.S4() + this.S4());
},
S4: function()
{
return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
},
get: function(key)
{
return window.localStorage.getItem(key);
},
set: function(key, val)
{
return window.localStorage.setItem(key, val);
},
/**
* Gets the current time left
*/
getTimer: function()
{
return this.get('timeoutCountdown');
},
/**
* Sets the time left
*
* @param curTime
*/
setTimer: function(curTime)
{
return this.set('timeoutCountdown', curTime);
},
/**
* Gets the tab that is controlling the timeout countdown
*/
getTab: function()
{
return this.get('timeoutTab');
},
/**
* Sets the tab that is controlling the timeout countdown
* @param tab
*/
setTab: function(tab)
{
return this.set('timeoutTab', tab);
},
/**
* Gets the last tab that updated the timer countdown
*/
getTabLast: function()
{
return this.get('timeoutTabLast');
},
/**
* Sets the last tab that updated the timer countdown
*/
setTabLast: function()
{
return this.set('timeoutTabLast', new Date());
},
/**
* Stops the countdown prior to timeout
*/
stopPriorCountdown: function()
{
if (timeout.priorCountDown)
{
timeout.priorCountDown = false;
}
},
/**
* Determines whether or not the timeout countdown alert should allow extending the session and then hiding the alert
*
* @returns {*}
*/
resetOnAlert: function()
{
if (!timeout.options.triggerResetOnAlert)
{
return $('#jTimeoutAlert').length === 0 && $('#jTimedoutAlert').length === 0;
}
return $('#jTimedoutAlert').length === 0 && timeout.options.triggerResetOnAlert;
},
/**
* Stops flashing in the title
*/
stopFlashing: function()
{
if (timeout.options.flashTitle)
{
if (timeout.flashingTitle)
{
window.clearInterval(timeout.flashingTitle);
}
timeout.flashingTitle = false;
document.title = timeout.options.originalTitle;
}
},
/**
* Starts flashing the title
*/
startFlashing: function()
{
if (!timeout.flashingTitle && timeout.options.flashTitle)
{
timeout.flashingTitle = window.setInterval(function ()
{
if (document.title === timeout.options.flashingTitleText)
{
document.title = timeout.options.originalTitle;
}
else
{
document.title = timeout.options.flashingTitleText;
}
}, timeout.options.flashTitleSpeed);
}
},
/**
* Starts the countdown prior to timeout
*
* @param elem
*/
startPriorCountdown: function(elem)
{
timeout.priorCountDown = elem;
},
/**
* Hides the countdown alert
*/
hideCountdownAlert: function()
{
timeout.timeoutWarning = false;
var timeoutAlert = $('#jTimeoutAlert');
if (timeoutAlert.length > 0)
{
timeoutAlert.closeAlert();
}
},
setMouseTimeout: function(timeout)
{
this.mouseTimeout = timeout;
},
stopMouseTimeout: function(){
if( this.mouseTimeout)
{
window.clearTimeout(this.mouseTimeout);
}
},
setCountdown: function(interval){
this.interval = interval;
},
stopCountdown: function()
{
if( this.interval )
{
window.clearInterval(this.interval);
}
},
/* The magic happens here. This function loops every x seconds */
countdown: function()
{
/* Get current timer, tab that reported that time, and the time that tab reported */
var seconds = timeout.getTimer(),
whichTab = timeout.getTab(),
whichTabLast = timeout.getTabLast();
/* If another tab updated it more than 2 seconds ago, this tab will take control */
if (whichTabLast < new Date('-2 seconds'))
{
seconds = seconds - Math.abs(((new Date()).getTime() - whichTabLast.getTime()) / 1000); //remove difference
whichTab = timeout.options.tabID;
timeout.setTab(whichTab);
}
if (timeout.priorCountDown)
{
timeout.priorCountDown.text(seconds);
}
/* If the last tab to interact is the same as this one */
if (whichTab === timeout.options.tabID)
{
seconds = seconds - timeout.options.heartbeat;
timeout.setTabLast();
timeout.setTimer(seconds);
}
/* Timeout */
if (seconds <= 0 && !timeout.timedOut)
{
timeout.timeoutWarning = true;
timeout.timedOut = true;
timeout.options.onTimeout(timeout);
timeout.stopCountdown();
timeout.stopMouseTimeout();
timeout.setTimer(0);
}
/* If less than x seconds left and not warned yet, show warning */
else if (seconds < timeout.options.secondsPrior && !timeout.timeoutWarning)
{
timeout.timeoutWarning = true;
timeout.startFlashing();
timeout.options.onPriorCallback(timeout, seconds);
}
/* reset timeout warning if timeout was set higher */
else if (seconds >= timeout.options.secondsPrior && timeout.timeoutWarning)
{
timeout.stopFlashing();
timeout.stopPriorCountdown();
timeout.hideCountdownAlert();
}
}
};
//if no tab id was provided, generate a unique tab id
if (!timeout.options.tabID)
{
timeout.options.tabID = timeout.generateUid();
}
/* Set defaults in localStorage (shared storage across tabs) */
timeout.setTimer(timeout.options.timeoutAfter);
timeout.setTab(timeout.options.tabID);
timeout.setTabLast();
var inMS = timeout.options.heartbeat * 1000;
timeout.setCountdown(window.setInterval(timeout.countdown, inMS));
if(timeout.options.extendOnMouseMove)
{
inMS = timeout.options.mouseDebounce * 1000;
timeout.mouseMoved = false;
//delay the initial mousemove watch for x seconds
timeout.setMouseTimeout(window.setTimeout(function ()
{
//on mouse move
$('body').on('mousemove', function ()
{
if (!timeout.mouseMoved && timeout.resetOnAlert())
{
timeout.mouseMoved = true;
timeout.setMouseTimeout(window.setTimeout(function ()
{
timeout.mouseMoved = false;
}, inMS));
timeout.options.onMouseMove(timeout);
}
});
}, inMS));
}
window.jTimeout = timeout;
return timeout;
};
$.jTimeout.reset = function (seconds)
{
seconds = typeof seconds !== 'undefined' ? seconds : $.jTimeout.defaults.timeoutAfter; //default to default timeoutAfter
$.jTimeout().set('timeoutCountdown', seconds); //set timeout countdown
};
$.jTimeout.defaults = {
'flashTitle': true, //whether or not to flash the tab/title bar when about to timeout, or after timing out
'flashTitleSpeed': 500, //how quickly to switch between the original title, and the warning text
'flashingTitleText': '**WARNING**', //what to show in the tab/title bar when about to timeout, or after timing out
'originalTitle': document.title, //store the original title of this page
'tabID': false, //each tab needs a unique ID so you can tell which one last updated the timer - false makes it autogenerate one
'timeoutAfter': 600, //pass this from server side to be fully-dynamic. For PHP: ini_get('session.gc_maxlifetime'); - 1440 is generally the default timeout
'heartbeat': 1, //how many seconds in between checking and updating the timer
'extendOnMouseMove': true, //Whether or not to extend the session when the mouse is moved
'mouseDebounce': 30, //How many seconds between extending the session when the mouse is moved (instead of extending a billion times within 5 seconds)
'onMouseMove': function(timeout){
//request the session extend page
//$.get(timeout.options.extendUrl);
//console.log("extend");
//reset timer
timeout.setTimer(timeout.options.timeoutAfter);
//set cur tab to this one
timeout.setTab(timeout.options.tabID);
timeout.setTabLast();
}, //Override the standard $.get() request that uses the extendUrl with your own function.
'extendUrl': '/dashboard', //URL to request in order to extend the session.
'logoutUrl': '/', //URL to request in order to force a logout after the timeout. This way you can end a session early based on a shorter timeout OR if the front-end timeout doesn't sync with the backend one perfectly, you don't look like an idiot.
'loginUrl': '/login', //URL to send a customer when they want to log back in
'secondsPrior': 60, //how many seconds before timing out to run the next callback (onPriorCallback)
'triggerResetOnAlert': false, // should it reset the timer with mouse move while the alert is visible and hide it?
//override the popup that shows when getting within x seconds of timing out
'onPriorCallback': function(timeout, seconds){
console.log("onPriorCallback");
timeout.options.extendOnMouseMove=false;
timeout.mouseMoved = true;
//$("#myModal").modal('show');
},
//override the click to extend button callback
'onClickExtend': function(timeout){
/* Request dashboard to increase session */
//$.get(timeout.options.extendUrl);
//console.log("extend");
timeout.setTimer(timeout.options.timeoutAfter);
timeout.setTab(timeout.options.tabID);
timeout.setTabLast();
},
//override the timeout function if you'd like
'onTimeout': function(timeout){
/* Alert User */
console.log('timeout');
/* Force logout */
$.get(timeout.options.logoutUrl);
}
};
/* END OF ON JQUERY LOAD */
})(jQuery);