Время ожидания аутентификации ASP.NET - программирование
Подтвердить что ты не робот

Время ожидания аутентификации ASP.NET

Это может быть очень простой вопрос, но через несколько часов, пытаясь понять, как это работает на ASP.NET 4.0, я до сих пор не знаю.

Я использую проверку подлинности с помощью форм. У меня есть страница входа с элементом управления им.

Это то, что мне нужно, когда пользователи заходят:

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

B- Если они нажали кнопку "Запомнить меня", они должны оставаться на связи до выхода из системы, независимо от того, закрыли ли они браузер или перезагрузили компьютер.

Проблема, с которой я столкнулся, когда они заходят в систему, я не вижу cookie на своем компьютере:

  • Где находится файл cookie? Является ли файл cookie для памяти?
  • Что произойдет, если сессия закончится? Я бы хотел, чтобы они регистрировались, если тайм-аут не завершен.
  • Что произойдет, если пул приложений будет переработан?

Также у меня есть другая проблема: когда они нажимают кнопку "запомнить меня" (случай B), я бы хотел, чтобы они вошли в систему, пока не нажмут кнопку выхода. На этот раз я вижу cookie, но похоже, что они остаются на связи только для тайм-аута... так в чем разница между воспоминанием меня или нет...

Я хочу полностью отделить аутентификацию и сеанс. Я бы хотел, чтобы проверка подлинности контролировалась с помощью файлов cookie, если не очень плохо подходит.

Спасибо за помощь -.

4b9b3361

Ответ 1

Обработка нестационарных, срочных истекших билетов

Аутентификация форм использует куки файл в памяти для билета, если вы не сделаете его постоянным (например, FormsAuthentication.SetAuthCookie(username, true) сделает его постоянным). По умолчанию билет использует скользящее окончание. Каждый раз, когда запрос обрабатывается, билет отправляется с новой датой истечения срока действия. По истечении этой даты cookie и билет оба недопустимы, и пользователь будет перенаправлен на страницу входа.

Аутентификация форм не имеет встроенной обработки для перенаправления страниц, которые уже были отображены, которые сидят дольше, чем таймаут. Вам нужно будет добавить это самостоятельно. На простейшем уровне вам нужно будет запустить таймер с загрузкой документа, используя JavaScript.

<script type="text/javascript">
  var redirectTimeout = <%FormsAuthentication.Timeout.TotalMilliseconds%>
  var redirectTimeoutHandle = setTimeout(function() { window.location.href = '<%FormsAuthentication.LoginUrl%>'; }, redirectTimeout);
</script>

С приведенным выше, если ваша страница не обновляется или не изменяется, или redirectTimeoutHandle в противном случае не отменяется (с помощью clearTimeout(redirectTimeoutHandle);), она будет перенаправлена ​​на страницу входа. Билет FormsAuth должен быть уже истек, поэтому вам не нужно ничего с этим делать.

Трюк здесь заключается в том, работает ли ваш сайт AJAX, или вы считаете, что другие события на стороне клиента являются активными действиями пользователя (перемещение или щелчок мышью и т.д.). Вам придется отслеживать эти события вручную и когда они происходят, reset redirectTimeoutHandle. Например, у меня есть сайт, который сильно использует AJAX, поэтому страница часто не обновляется физически. Поскольку я использую jQuery, я могу иметь его reset тайм-аут каждый раз, когда выдается запрос AJAX, что должно, по сути, приводить к перенаправлению страницы, если они сидят на одной странице и не делают никаких обновлений.

Здесь полная инициализация script.

$(function() {
   var _redirectTimeout = 30*1000; // thirty minute timeout
   var _redirectUrl = '/Accounts/Login'; // login URL

   var _redirectHandle = null;

   function resetRedirect() {
       if (_redirectHandle) clearTimeout(_redirectHandle);
       _redirectHandle = setTimeout(function() { window.location.href = _redirectUrl; }, _redirectTimeout);
   }

   $.ajaxSetup({complete: function() { resetRedirect(); } }); // reset idle redirect when an AJAX request completes

   resetRedirect(); // start idle redirect timer initially.
});

Просто отправив запрос AJAX, время ожидания на стороне клиента и билет (в виде файла cookie) будут обновлены, и ваш пользователь будет в порядке.

Однако, если активность пользователя не приводит к обновлению билета FormsAuth, пользователь, как представляется, будет выходить из системы при следующем запросе новой страницы (либо путем навигации, либо через AJAX). В этом случае вам нужно будет "ping" вашего веб-приложения, когда действие пользователя происходит с вызовом AJAX, например, пользовательским обработчиком, действием MVC и т.д., Чтобы обновить ваш билет FormsAuth. Обратите внимание, что вам нужно быть осторожным при проверке на сервере, чтобы поддерживать актуальность, так как вы не хотите наводнять сервер запросами, так как они, скажем, перемещают курсор или нажимают на вещи. Здесь добавление к init script выше, которое добавляет resetRedirect к щелчкам мыши по документу в дополнение к начальной загрузке страницы и запросам AJAX.

$(function() {
   $(document).on('click', function() {
      $.ajax({url: '/ping.ashx', cache: false, type: 'GET' }); // because of the $.ajaxSetup above, this call should result in the FormsAuth ticket being updated, as well as the client redirect handle.
   });
});

Обработка "постоянных" билетов

Вам нужен билет, который будет отправлен клиенту как постоянный файл cookie с произвольным длительным таймаутом. Вы должны иметь возможность оставить клиентский код и web.config, как они есть, но обрабатывать предпочтение пользователя для постоянного билета отдельно в логике входа. Здесь вам нужно будет изменить билет. Ниже приведена логика на странице входа в систему:

// assumes we have already successfully authenticated

if (rememberMe)
{
    var ticket = new FormsAuthenticationTicket(2, userName, DateTime.Now, DateTime.Now.AddYears(50), true,
                                               string.Empty, FormsAuthentication.FormsCookiePath);
    var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(ticket))
                     {
                         Domain = FormsAuthentication.CookieDomain,
                         Expires = DateTime.Now.AddYears(50),
                         HttpOnly = true,
                         Secure = FormsAuthentication.RequireSSL,
                         Path = FormsAuthentication.FormsCookiePath
                     };
    Response.Cookies.Add(cookie);
    Response.Redirect(FormsAuthentication.GetRedirectUrl(userName, true));
}
else
{
    FormsAuthentication.RedirectFromLoginPage(userName, false);
}

Бонус: сохранение ролей в билете

Вы спросили, можете ли вы сохранить роли в билете/файле cookie, чтобы вам не пришлось снова искать их. Да, это возможно, но есть некоторые соображения.

  • Вы должны ограничить количество данных, которые вы положили в билет, потому что файлы cookie могут быть только такими большими
  • Вам следует подумать о том, следует ли кэшировать роли на клиенте.

Чтобы подробнее остановиться на № 2:

Вы не должны неявно доверять утверждениям, которые вы получаете от пользователя. Например, если пользователь входит в систему и является администратором, и проверяет "помнить меня", получая, таким образом, постоянный долгосрочный билет, он будет Admin навсегда (или пока этот файл cookie не истечет или не будет удален). Если кто-то удалит их из этой роли в вашей базе данных, приложение по-прежнему будет считать их администратором, если у них есть старый билет. Таким образом, вам может быть лучше получать роли пользователя каждый раз, но кэширование ролей в экземпляре приложения в течение определенного периода времени для минимизации работы базы данных.

Технически это тоже проблема для самого билета. Опять же, вы не должны доверять этому только потому, что у них есть действительный билет, который по-прежнему действителен. Вы можете использовать аналогичную логику, как и роли: убедитесь, что пользователь, на который ссылается билет, по-прежнему существует и действителен (что он не заблокирован, отключен или удален) путем запроса вашей фактической базы данных и просто кэширования результатов db на период чтобы улучшить производительность. Это то, что я делаю в своих приложениях, где билет рассматривается как требование идентификации (аналогичным образом, имя пользователя/пароль - это другой тип претензии). Вот упрощенная логика в файле global.asax.cs(или в модуле HTTP):

protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
  var application = (HttpApplication)sender;
  var context = application.Context;  

  EnsureContextUser(context);
}

private void EnsureContextUser(HttpContext context)
{
   var unauthorizedUser = new GenericPrincipal(new GenericIdentity(string.Empty, string.Empty), new string[0]);

   var user = context.User;

   if (user != null && user.Identity.IsAuthenticated && user.Identity is FormsIdentity)
   {
      var ticket = ((FormsIdentity)user.Identity).Ticket;

      context.User = IsUserStillActive(context, ticket.Name) ? new GenericPrincipal(user.Identity, GetRolesForUser(context, ticket.Name)) : unauthorizedUser;

      return; 
   }

   context.User = unauthorizedUser;
}

private bool IsUserStillActive(HttpContext context, string username)
{
   var cacheKey = "IsActiveFor" + username;
   var isActive = context.Cache[cacheKey] as bool?

   if (!isActive.HasValue)
   {
      // TODO: look up account status from database
      // isActive = ???
      context.Cache[cacheKey] = isActive;
   }

   return isActive.GetValueOrDefault();
}

private string[] GetRolesForUser(HttpContext context, string username)
{
   var cacheKey = "RolesFor" + username;
   var roles = context.Cache[cacheKey] as string[];

   if (roles == null)
   {
      // TODO: lookup roles from database
      // roles = ???
      context.Cache[cacheKey] = roles;
   }

   return roles;
}

Конечно, вы можете решить, что вам все равно, и просто хотите доверять билет и сохранить роли в билете. Во-первых, мы обновляем логику входа выше:

// assumes we have already successfully authenticated

if (rememberMe)
{
    var ticket = new FormsAuthenticationTicket(2, userName, DateTime.Now, DateTime.Now.AddYears(50), true, GetUserRolesString(), FormsAuthentication.FormsCookiePath);
    var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(ticket))
                     {
                         Domain = FormsAuthentication.CookieDomain,
                         Expires = DateTime.Now.AddYears(50),
                         HttpOnly = true,
                         Secure = FormsAuthentication.RequireSSL,
                         Path = FormsAuthentication.FormsCookiePath
                     };
    Response.Cookies.Add(cookie);
    Response.Redirect(FormsAuthentication.GetRedirectUrl(userName, true));
}
else
{
    var ticket = new FormsAuthenticationTicket(2, userName, DateTime.Now, DateTime.Now.AddMinutes(FormsAuthentication.Timeout), false, GetUserRolesString(), FormsAuthentication.FormsCookieName);
    var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(ticket))
       {
          Domain = FormsAuthentication.CookieDomain,
          HttpOnly = true,
          Secure = FormsAuthentication.RequireSSL,
          Path = FormsAuthentication.FormsCookiePath
       };
    Response.Cookies.Add(cookie);
    Response.Redirect(FormsAuthentication.GetRedirectUrl(userName, false));
}

Добавить метод:

   private string GetUserRolesString(string userName)
   {
        // TODO: get roles from db and concatenate into string
   }

Обновите свой файл global.asax.cs, чтобы получить роли из билета и обновить HttpContext.User:

protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
  var application = (HttpApplication)sender;
  var context = application.Context;  

  if (context.User != null && context.User.Identity.IsAuthenticated && context.User.Identity is FormsIdentity)
  {
      var roles = ((FormsIdentity)context.User.Identity).Ticket.Data.Split(",");

      context.User = new GenericPrincipal(context.User.Identity, roles);
  }
}

Ответ 2

Для A вам нужно будет установить переменную тайм-аута сеанса так долго, пока вы хотите, чтобы пользователь оставался вошедшим в систему для Timeout свойство задает период ожидания, назначенный объекту Session для приложения, в минутах. Если пользователь не обновляет или не запрашивает страницу в течение периода ожидания, сеанс завершается.

Для части B Я бы предложил сохранить это значение в переменной сеанса (или cookie, но не на сервере) и проверить это значение в событии Session_End в global.asax файл. Если он установлен, обновите сеанс.

Событие Session_End не срабатывает, когда браузер закрыт, он запускается, когда сервер не получил запрос от пользователя за определенный период времени (по умолчанию 20 минут).