Предупреждение о тайм-ауте Javascript / поддержание активности, когда ASP.NET MVC 5 Аутентификация куки-файла идентификации SlidingExpiration не обновляет куки-файл до тех пор, пока не истечет половина времени? - PullRequest
0 голосов
/ 09 апреля 2019

У нас есть рабочее всплывающее предупреждение о превышении времени ожидания входа в систему в javascript, в котором отображаются кнопки «Работать» и «Выход», когда пользователь собирается выйти из системы. Keep Working вызывает действие контроллера ASP.NET MVC, которое возвращает true, и мы подумали, что это будет держать пользователя вошедшим в систему, но SlidingExpiration = true приводит к тому, что срок действия будет сброшен только в том случае, если запрос сделан после того, как срок действия истек на половине пути. Поэтому мы запускаем таймер при каждой загрузке страницы в зависимости от срока действия файла cookie, и если пользователь перемещается по приложению, таймер JS сбрасывается при каждой загрузке, но файл cookie не обновляется, поэтому срок действия файла cookie истекает до того, как пользователь может нажать «Продолжить работу». Поэтому, если они ждут последние пару секунд и нажимают «Продолжить работу», так как таймер JavaScript сбрасывается при каждой загрузке страницы, а cookie не обновляется до половины пути, пользователь уже вышел из системы к тому моменту, как он нажал «Продолжить работу», и его выгнали. в логин, и они теряют свою работу.

Так, как мы можем заставить его обновлять истечение срока аутентификации cookie при каждой загрузке? Или есть какой-то другой способ добиться этого с помощью SlidingExpiration?

Пример:

В конечном итоге мы установим срок действия до 20 минут и предупредим через 2 минуты, но сейчас проводим тестирование с истечением 60 секунд и предупреждением 15 секунд.

Мы вычитаем 10 секунд из срока действия, 60-10 = 50 секунд, в качестве буфера, поскольку сервер и клиент будут немного не синхронизированы из-за времени загрузки. Поэтому, если пользователь ждет, пока таймер не станет равным 0, он все равно должен войти в систему, когда вызывается API «Keep Working» или «Logout». Но в данном случае это не так.

Вот график времени:

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, устанавливая ValidateInterval в OnValidateIdentity, устанавливая ExpireTimeSpan в SignIn (), устанавливая CookieHttpOnly в false, устанавливая cache: false в вызове $ .ajax (), и, похоже, это не исправляет или изменить что-либо.

Кто-нибудь знает, что мне не хватает или какие настройки могут решить мою проблему? Об этом написано несколько сообщений, но многие из них являются веб-формами, и другие решения, похоже, не работают. Спасибо за ваше время!

Вот код: Частичное представление SessionTimeout для инициализации компонента JS с тем же сроком действия, что и в файле web.config.

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;
}

Startup.Auth.cs с конфигурацией проверки подлинности cookie

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

AccountController с логином и SignInAsync

    [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);
    }

Web.config с ExpiresInSeconds

<appSettings>    
    <add key="LoginCookieExpireInSeconds" value="60" /><!--1200 = 20 mins-->
</appSettings>

Действие контроллера Keep Alive:

public JsonResult IsSessionAlive()
{
    return Json(true, JsonRequestBehavior.AllowGet);
}

Действие контроллера выхода из системы, на самом деле не применимо:

[HttpPost]
public ActionResult LogOff()
{
    AuthenticationManager.SignOut();
    return RedirectToAction("Login", "Account");
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...