当ASP.NET MVC 5身份Cookie身份验证SlidingExpiration在一半过期之前不刷新cookie时,Javascript超时警告/保持活动状态。

时间:2019-04-09 14:49:40

标签: javascript c# asp.net-mvc authentication cookies

我们在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");
}

0 个答案:

没有答案