Что касается скользящего истечения срока аутентификации и сеанса форм ASP.NET

У нас есть приложение ASP.NET 4.5 WebForms, использующее функции проверки подлинности собственных форм и сеанса. Оба имеют тайм-аут в течение 20 минут с истечением срока годности.

Представьте себе следующий сценарий. Пользователь некоторое время работал в нашем приложении, а затем продолжает делать некоторые другие вещи, оставляя наше приложение незанятым на 20 минут. Затем пользователь возвращается в наше приложение, чтобы написать отчет. Однако, когда пользователь пытается сохранить, он/она обрабатывается экраном входа, и отчет потерян.

Очевидно, это нежелательно. Вместо этого сценария мы хотим, чтобы браузер был перенаправлен на страницу входа в тот момент, когда истек срок аутентификации или сеанса. Чтобы понять это, мы создали службу Web Api, которую можно вызвать, чтобы проверить, так ли это.

public class SessionIsActiveController : ApiController
{
    /// <summary>
    /// Gets a value defining whether the session that belongs with the current HTTP request is still active or not.
    /// </summary>
    /// <returns>True if the session, that belongs with the current HTTP request, is still active; false, otherwise./returns>
    public bool GetSessionIsActive()
    {
        CookieHeaderValue cookies = Request.Headers.GetCookies().FirstOrDefault();
        if (cookies != null && cookies["authTicket"] != null && !string.IsNullOrEmpty(cookies["authTicket"].Value) && cookies["sessionId"] != null && !string.IsNullOrEmpty(cookies["sessionId"].Value))
        {
            var authenticationTicket = FormsAuthentication.Decrypt(cookies["authTicket"].Value);
            if (authenticationTicket.Expired) return false;
            using (var asdc = new ASPStateDataContext()) // LINQ2SQL connection to the database where our session objects are stored
            {
                var expirationDate = SessionManager.FetchSessionExpirationDate(cookies["sessionId"].Value + ApplicationIdInHex, asdc);
                if (expirationDate == null || DateTime.Now.ToUniversalTime() > expirationDate.Value) return false;
            }
            return true;
        }
        return false;
    }
}

Эта служба Web Api вызывается каждые 10 секунд клиентом для проверки того, истекло ли аутентификация или сеанс. Если это так, script перенаправляет браузер на страницу входа. Это работает как шарм.

Однако вызов этой службы вызывает скользящее завершение как аутентификации, так и сеанса. Таким образом, по существу, создается бесконечная аутентификация и сеанс. Я установил точку останова в начале службы, чтобы проверить, является ли это одной из наших собственных функций, которая запускает это. Но это не так, кажется, что где-то еще глубже в ASP.NET, до выполнения службы.

  • Есть ли способ отключить запуск аутентификации ASP.NET и истечения срока действия сеанса для конкретного запроса?
  • Если нет, то что лучше всего для решения такого сценария?
+16
источник поделиться
3 ответа
  • Это кажется невозможным. После того, как разрешение на выключение включено, оно всегда срабатывает. Если есть доступ к сеансу без его расширения, мы не смогли его найти.

  • Итак, как решить этот сценарий? Мы пришли к следующему альтернативному решению, которое первоначально было предложено в вопросе. Это действительно более эффективно, потому что он не использует веб-службу для дома домой каждые х секунд.

Таким образом, мы хотим узнать, когда устарела или прошла аутентификация или сеанс ASP.NET, поэтому мы можем активно выходить из системы пользователем. Простой javascript-таймер на каждой странице (как предлагалось Халидом Абухакмехом) не будет достаточным, потому что пользователь может одновременно работать с приложением в нескольких окнах/вкладках браузера.

Первое решение, которое мы сделали для упрощения этой проблемы, - сделать время истечения срока действия сеанса на несколько минут дольше, чем время истечения срока действия проверки подлинности форм. Таким образом, сеанс никогда не истечет до проверки подлинности форм. Если в предыдущем сеансе есть затяжная старая сессия, пользователь в следующий раз попытается войти в систему, мы оставим ее, чтобы создать новую новую.

Хорошо, так что теперь нам нужно только учитывать срок действия проверки подлинности форм.

Затем мы решили отключить автоматическое скользящее окончание аутентификации форм (как установлено в web.config) и создать нашу собственную версию.

public static void RenewAuthenticationTicket(HttpContext currentContext)
{
    var authenticationTicketCookie = currentContext.Request.Cookies["AuthTicketNameHere"];
    var oldAuthTicket = FormsAuthentication.Decrypt(authenticationTicketCookie.Value);
    var newAuthTicket = oldAuthTicket;
    newAuthTicket = FormsAuthentication.RenewTicketIfOld(oldAuthTicket); //This triggers the regular sliding expiration functionality.
    if (newAuthTicket != oldAuthTicket)
    {
        //Add the renewed authentication ticket cookie to the response.
        authenticationTicketCookie.Value = FormsAuthentication.Encrypt(newAuthTicket);
        authenticationTicketCookie.Domain = FormsAuthentication.CookieDomain;
        authenticationTicketCookie.Path = FormsAuthentication.FormsCookiePath;
        authenticationTicketCookie.HttpOnly = true;
        authenticationTicketCookie.Secure = FormsAuthentication.RequireSSL;
        currentContext.Response.Cookies.Add(authenticationTicketCookie);
        //Here we have the opportunity to do some extra stuff.
        SetAuthenticationExpirationTicket(currentContext);
    }
}

Мы вызываем этот метод из события OnPreRenderComplete в нашем классе BasePage приложения, из которого наследуется каждая другая страница. Он делает то же самое, что и функциональность обычного сползания, но мы получаем возможность делать некоторые дополнительные вещи; как вызов нашего метода SetAuthenticationExpirationTicket.

public static void SetAuthenticationExpirationTicket(HttpContext currentContext)
{
    //Take the current time, in UTC, and add the forms authentication timeout (plus one second for some elbow room ;-)
    var expirationDateTimeInUtc = DateTime.UtcNow.AddMinutes(FormsAuthentication.Timeout.TotalMinutes).AddSeconds(1);
    var authenticationExpirationTicketCookie = new HttpCookie("AuthenticationExpirationTicket");
    //The value of the cookie will be the expiration date formatted as milliseconds since 01.01.1970.
    authenticationExpirationTicketCookie.Value = expirationDateTimeInUtc.Subtract(new DateTime(1970, 1, 1)).TotalMilliseconds.ToString("F0");
    authenticationExpirationTicketCookie.HttpOnly = false; //This is important, otherwise we cannot retrieve this cookie in javascript.
    authenticationExpirationTicketCookie.Secure = FormsAuthentication.RequireSSL;
    currentContext.Response.Cookies.Add(authenticationExpirationTicketCookie);
}

Теперь у нас есть дополнительный файл cookie, который всегда представляет собой правильное время истечения срока аутентификации, даже если пользователь работает в разных окнах/вкладках браузера. В конце концов, файлы cookie имеют широкий охват браузера. Теперь остается только функция javascript для проверки значения cookie.

function CheckAuthenticationExpiration() {
    var c = $.cookie("AuthenticationExpirationTicket");
    if (c != null && c != "" && !isNaN(c)) {
        var now = new Date();
        var ms = parseInt(c, 10);
        var expiration = new Date().setTime(ms);
        if (now > expiration) location.reload(true);
    }
}

(Обратите внимание, что мы используем jQuery Cookie Plugin для извлечения файла cookie.)

Поместите эту функцию в интервал, и пользователи будут выходить из системы в тот момент, когда истек срок его аутентификации. Voilà:-) Дополнительным преимуществом этой реализации является то, что теперь у вас есть контроль над тем, когда срок действия проверки подлинности форм расширяется. Если вам нужна куча веб-сервисов, которые не продлевают срок действия, просто не вызывайте для них метод RenewAuthenticationTicket.

Пожалуйста, оставьте комментарий, если вам есть что добавить!

+13
источник

Все это можно решить на стороне клиента, без необходимости возвращаться на сервер.

В JavaScript сделайте это.

 var timeout = setTimeout(function () {
      window.location = "/login";
 }, twentyMinutesInMilliseconds + 1);

Тайм-аут будет установлен на 20 минут при каждом обновлении страницы. Это гарантирует, что пользователь должен выполнить всю свою работу до истечения тайм-аута. Многие сайты используют этот метод, и это избавляет вас от ненужных запросов сервера.

+1
источник

Функциональность вашего сайта должна работать без JavaScript или вы просто заменяете одну проблему другой. Я также решил эту проблему и вот как она была решена:

Когда вы выполняете аутентификацию, тогда cookie сеанса создается со временем жизни по умолчанию на 20 минут. По истечении этого срока пользователь выйдет из системы.

cookie image

Когда пользователь выбирает "запомнить меня" в форме в, тогда на клиентской стороне и в базе данных создается дополнительный файл cookie [AuthCookie]. Этот файл cookie имеет продолжительность 1 месяц. Всякий раз, когда страница загружается, данные cookie сеанса и персистентности воссоздаются с новым временем жизни (обычно вы хотите дешифровать/склеить билет).

Представьте себе следующий сценарий. Пользователь работал в нашей заявке на какое-то время, а затем переходит к другим вещам, оставив приложение простаивает в течение 20 минут. Затем пользователь возвращается к нашей приложение для написания отчета. Когда пользователь пытается сохранить, его сеанс восстанавливается перед запросом.

Один из способов сделать это - расширить global.aspx, чтобы обработать предварительный запрос. Что-то в строках:

    void application_PreRequestHandlerExecute(object sender, EventArgs e){
        ...
        if (HttpContext.Current.Handler is IRequiresSessionState) {
            if (!context.User.Identity.IsAuthenticated)
                AuthService.DefaultProvider.AuthenticateUserFromExternalSource();

AuthenticateUserFromExternalSource должен проверить, соответствуют ли данные cookie с базой данных, потому что все, что хранится на стороне клиента, может быть изменено. Если у вас есть платные услуги с правами доступа, вам необходимо проверить, все ли у пользователя эти права, и затем вы можете воссоздать сеанс.

+1
источник

Посмотрите другие вопросы по меткам или Задайте вопрос