2012-10-15 4 views
14

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

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

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

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

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

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

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

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

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

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

ответ

27

Обработка непостоянного, Sliding EXPIRATION БИЛЕТЫ

Forms Authentication использует куки в памяти за билет, если вы не сделаете его постоянным (например, 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, или вы рассматриваете другие события на стороне клиента как активную активность пользователя (перемещение или щелчок мышью и т. Д.). Вам нужно будет отслеживать эти события вручную и когда они происходят, сбросьте redirectTimeoutHandle. Например, у меня есть сайт, который сильно использует AJAX, поэтому страница часто не обновляется физически. Поскольку я использую jQuery, я могу перезапустить тайм-аут каждый раз, когда выдается запрос AJAX, что должно, по сути, приводить к перенаправлению страницы, если они сидят на одной странице и не делают никаких обновлений.

Вот полный сценарий инициализации.

$(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, на стороне клиента тайм-аут и билет (в виде печенья) оба будут обновляться, а пользователь должен быть в порядке.

Однако, если действие пользователя не приводит к обновлению билета FormsAuth, пользователь, как представляется, будет выходить из системы при следующем запросе новой страницы (либо путем навигации, либо через AJAX). В этом случае вам нужно будет «ping» вашего веб-приложения, когда действие пользователя происходит с вызовом AJAX, например, пользовательским обработчиком, действием MVC и т. Д., Чтобы обновить ваш билет FormsAuth. Обратите внимание, что вам нужно быть осторожным при проверке на сервере, чтобы поддерживать актуальность, так как вы не хотите наводнять сервер запросами, так как они, скажем, перемещают курсор или нажимают на вещи. Ниже приведено дополнение к скрипту init, который добавляет 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. 
    }); 
}); 

Handling Билеты «Постоянные»

Вам нужен билет будет отправлен клиенту как постоянное печенье с произвольно долгое время ожидания. Вы должны оставить клиентский код и 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); 
} 

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

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

  1. Вы должны ограничить объем данных, вы положили в билете, потому что печенье может быть только настолько большим
  2. Вы должны рассмотреть вопрос о целесообразности или нет роли должны быть кэшируются на клиенте.

Чтобы уточнить # 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 
    } 

Update ваши 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); 
    } 
} 
+0

Спасибо, я думаю, что куки-файлы в памяти уходят после окончания сеанса или утилизации пула, верно? где я должен разместить FormsAuthentication.SetAuthCookie (имя пользователя, истина), чтобы создать постоянный файл cookie? –

+0

Нет, он хранится в памяти браузером клиента. До тех пор, пока cookie не ударит по истечении срока действия, cookie будет по-прежнему действительным, даже если пул приложений будет переработан. Формы Auth не зависят от состояния сеанса, поэтому все, что вы делаете в сеансе, не должно влиять на билет. Что касается создания файла cookie/билета и его настойчивости, вам не обязательно использовать SetAuthCookie. Например, вы можете использовать перегрузку для «RedirectFromLoginPage» (см. Http://msdn.microsoft.com/en-us/library/ka5ffkce.aspx). Просто используйте это, когда вы в настоящее время регистрируете пользователей. – HackedByChinese

+0

Кроме того, я не буду полагаться на состояние сеанса для связанных с безопасностью материалов. Сеансы могут запускаться и заканчиваться произвольно, прекращаются, когда пул приложений перерабатывается (если вы не используете отдельный процесс или базу данных для хранения состояния сеанса) и работает против механизмов, которые ASP.NET имеет для управления аутентификацией пользователя. – HackedByChinese

0

Для А вы хотите установить переменный таймаут сеанса, однако долго вы хотите, чтобы пользователь оставаться в системе для The Timeout property specifies the time-out period assigned to the Session object for the application, in minutes. If the user does not refresh or request a page within the time-out period, the session ends.

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

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

+0

Спасибо. Для A: я хочу отделить сеанс и аутентификацию, я не хочу, чтобы у аутентификации ничего не было с Session. Также я обнаружил, что, устанавливая slideExpiration = "1" (по умолчанию), тайм-аут возвращается, когда пользователь что-то делает. Для B: сеанс не является решением, так как если пользователь вернется на следующий день, ему придется снова войти в систему, если я не настрою тайм-аут сеанса больше, чем 1 день, который не очень хорош. –

Смежные вопросы