我们在javascript中弹出了一个工作登录超时警告弹出窗口,当用户即将注销时,该窗口显示“保持工作”和“退出”按钮。 Keep Working调用一个返回true的ASP.NET MVC控制器操作,我们认为这将使用户保持登录状态,但是SlidingExpiration = true会导致过期被重置,仅当过期一半以上后才发出请求。因此,我们会根据Cookie的过期时间在每次页面加载时启动一个计时器,如果用户在应用程序中导航,则JS计时器会在每次加载时重置,但Cookie不会刷新,因此Cookie会在用户单击“保持工作”之前过期。因此,如果他们等到最后几秒钟,然后单击“保持工作”,由于javascript计时器会在每次页面加载时重置,并且Cookie直到中途才会刷新,因此当用户单击“保持工作”并被踢出时,用户已经注销退出登录并失去工作。
那么我们如何才能使它在每次加载时刷新cookie验证过期?还是在给定SlidingExpiration之后必须实现其他方式?
示例:
我们最终会将到期时间设置为20分钟,并在剩余2分钟时发出警告,但我们现在正在测试60秒到期时间和15秒警告。
我们从到期时间中减去10秒,即60-10 = 50秒,作为缓冲区,因为服务器和客户端由于下载时间而有些不同步。因此,如果用户一直等到计时器计数到0,则当调用“保持工作” API或“注销”时,他们仍应登录。但是在这种情况下,它们不是。
以下是时间表:
9:00:00-登录,将Cookie过期设置为9:01:00,加载视图,将计时器重置为50秒
9:00:06-导航至新视图,未刷新Cookie,计时器返回50秒
9:00:12-导航到新视图,未刷新Cookie,计时器返回50秒
9:00:18-导航到新视图,未刷新Cookie,计时器恢复为50秒
9:00:53-35秒后出现超时警告,提示“保持工作”,“退出”
9:01:00-Cookie过期,不再通过身份验证
9:01:03-10秒钟后,在倒数5时单击“保持工作”以保持登录状态,但是现在您不再登录,因此您将登录并失去工作。
我尝试过SlidingExpiration = false,在OnValidateIdentity中设置ValidateInterval,在SignIn()中设置ExpireTimeSpan,将CookieHttpOnly设置为false,在$ .ajax()调用中设置cache:false,但它似乎无法解决或 改变任何东西。
有人知道我可能会缺少什么或者什么设置可以解决我的问题吗?关于此的文章很多,但许多都是Web窗体,其他解决方案似乎不起作用。谢谢您的宝贵时间!
这是代码: SessionTimeout部分视图,用于从web.config中以相同的到期时间初始化JS组件。
var UserIsAuthenticated = '@(User.Identity.IsAuthenticated ? "true" : "false" )';
SessionTimeout.TimeoutSeconds = '@( Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings.Get("LoginCookieExpireInSeconds")))';
SessionTimeout.KeepAliveURL = '@(Url.Action("IsSessionAlive", "Account"))';
SessionTimeout.NotifyAtSeconds = 15;
if (eval(UserIsAuthenticated)) {//Don't Show on Login
SessionTimeout.Init();
}
工作会话超时Javascript具有1秒计时器,用于检查何时显示警告并跟踪/显示剩余时间的倒计时。代码示例小部件不喜欢JS,因此我删除了以下var语句,该语句是第一行和结尾的括号var SessionTimeout = {
TimeoutSeconds: 1200
, NotifyAtSeconds: 120
, CountdownSeconds:0
, KeepAliveURL: "" //URL to check if session is alive
, WarningTimeout: null
, LogoutFormName: "logoutForm"
, ResetCountdown: function () {
SessionTimeout.CountdownSeconds = SessionTimeout.TimeoutSeconds - 10;
}
, Init: function () {
$(document).ready(function () {
$(document.body).prepend('<div class="timer-popup"><div class="timer-cover"></div><div class="timer-content"><p>Your session is about to expire in <span id="timeoutCountDown"></span></p><p>Do you want to keep working?</p><button id="btnKeepWorking" class="keep-editing" onclick="SessionTimeout.Continue()">Keep Working</button><button id="btnLogout" class="leave" onclick="SessionTimeout.Logout()">Log out</button></div></div>')
SessionTimeout.ResetCountdown();
SessionTimeout.CheckCountdown();
SessionTimeout.WarningTimeout = window.setInterval("SessionTimeout.CheckCountdown()", 1000);
});
}
, CheckCountdown: function () {
var date = new Date;
console.log(SessionTimeout.CountdownSeconds + ' - ' + date.getHours() + ':' + date.getMinutes() + ':' + date.getSeconds());
if (SessionTimeout.CountdownSeconds >= 0)
{
$('#timeoutCountDown').text(SessionTimeout.FormatCountdown());
if (SessionTimeout.CountdownSeconds == 0)
{
clearInterval(SessionTimeout.WarningTimeout);
SessionTimeout.Logout();
}
else if (SessionTimeout.CountdownSeconds == SessionTimeout.NotifyAtSeconds)
{
$(document.body).addClass("timeout-blur");
$(".timer-popup").show();
$('.keep-editing').focus();//This is required for the cookie expiration to be refreshed. Not sure why though.
}
}
SessionTimeout.CountdownSeconds--;
}
, Continue: function () {
$.ajax({
url: SessionTimeout.KeepAliveURL,
success: function (alive) {
if (alive) {
SessionTimeout.ResetCountdown();
$(".timer-popup").hide();
$(document.body).removeClass("timeout-blur");
}
else {
window.location.reload();
}
},
error: function (e) { alert(JSON.stringify(e)); alert("An error occured while continuing your session."); }
});
}
, Logout: function () {
$("#" + SessionTimeout.LogoutFormName).submit();
}
, FormatCountdown: function () {
var sec_num = SessionTimeout.CountdownSeconds; // don't forget the second param
var hours = Math.floor(sec_num / 3600);
var minutes = Math.floor((sec_num - (hours * 3600)) / 60);
var seconds = sec_num - (hours * 3600) - (minutes * 60);
if (hours < 10) {hours = "0"+hours;}
if (minutes < 10) {minutes = "0"+minutes;}
if (seconds < 10) {seconds = "0"+seconds;}
return minutes+':'+seconds;
}
具有Cookie身份验证配置的Startup.Auth.cs
public void ConfigureAuth(IAppBuilder app)
{
int expireInSeconds = 1200; // 20 mins
int temp = 0;
if (int.TryParse(ConfigurationManager.AppSettings["LoginCookieExpireInSeconds"], out temp))
{
expireInSeconds = temp;
}
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
CookieSecure = CookieSecureOption.SameAsRequest,
SlidingExpiration = true,
ExpireTimeSpan = new System.TimeSpan(0, 0, expireInSeconds),
CookieName = "MY_COOKIE_NAME",
Provider = new CookieAuthenticationProvider
{
OnApplyRedirect = ctx =>
{
if (!IsAjaxRequest(ctx.Request))
{
ctx.Response.Redirect(ctx.RedirectUri);
}
}
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
}
private static bool IsAjaxRequest(IOwinRequest request)
{
IReadableStringCollection query = request.Query;
if ((query != null) && (query["X-Requested-With"] == "XMLHttpRequest"))
{
return true;
}
IHeaderDictionary headers = request.Headers;
return ((headers != null) && (headers["X-Requested-With"] == "XMLHttpRequest"));
}
具有登录和SignInAsync的AccountController
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
[ValidateInput(false)]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
ApplicationUser user = null;
if (ModelState.IsValid)
{
//Check PW
user = ValidateUserPasswordAndGroup(model.UserName, model.Password, model.ImpersonateUserName, null);
//Now generate the claim token for the user
if (user != null)// && user.Roles != null && user.Roles.Count > 0
{
OsuLDAPUserManager.InsertAuthenticatedUserRoles(user.UserName, this.Session.SessionID, new string[0], returnUrl, this.Request.ServerVariables.Get("LOCAL_ADDR"), this.Request.Url.AbsoluteUri, this.Request.ServerVariables.Get("APPL_PHYSICAL_PATH"));//Audit to DB
await SignInAsync(user, model.RememberMe);
return RedirectToLocal(returnUrl);
}
}
// If we got this far, something failed, redisplay form
if (user != null)
{
//Not in user group
ModelState.AddModelError("", "You are not in any user group. Please contact administrator if you need access.");
}
else
{
ModelState.AddModelError("", "Invalid username or password.");
}
return View(model);
}
带有ExpiresInSeconds的Web.config
<appSettings>
<add key="LoginCookieExpireInSeconds" value="60" /><!--1200 = 20 mins-->
</appSettings>
保持活动控制器动作:
public JsonResult IsSessionAlive()
{
return Json(true, JsonRequestBehavior.AllowGet);
}
注销控制器操作,不是真正适用:
[HttpPost]
public ActionResult LogOff()
{
AuthenticationManager.SignOut();
return RedirectToAction("Login", "Account");
}