Обработка непостоянного, 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);
}
Бонус: Сохранение ролей в билете
Вы спросили, если вы можете хранить роль в билете/куки, так что вы не имеете снова взглянуть на них. Да, это возможно, но есть некоторые соображения.
- Вы должны ограничить объем данных, вы положили в билете, потому что печенье может быть только настолько большим
- Вы должны рассмотреть вопрос о целесообразности или нет роли должны быть кэшируются на клиенте.
Чтобы уточнить # 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);
}
}
Спасибо, я думаю, что куки-файлы в памяти уходят после окончания сеанса или утилизации пула, верно? где я должен разместить FormsAuthentication.SetAuthCookie (имя пользователя, истина), чтобы создать постоянный файл cookie? –
Нет, он хранится в памяти браузером клиента. До тех пор, пока cookie не ударит по истечении срока действия, cookie будет по-прежнему действительным, даже если пул приложений будет переработан. Формы Auth не зависят от состояния сеанса, поэтому все, что вы делаете в сеансе, не должно влиять на билет. Что касается создания файла cookie/билета и его настойчивости, вам не обязательно использовать SetAuthCookie. Например, вы можете использовать перегрузку для «RedirectFromLoginPage» (см. Http://msdn.microsoft.com/en-us/library/ka5ffkce.aspx). Просто используйте это, когда вы в настоящее время регистрируете пользователей. – HackedByChinese
Кроме того, я не буду полагаться на состояние сеанса для связанных с безопасностью материалов. Сеансы могут запускаться и заканчиваться произвольно, прекращаются, когда пул приложений перерабатывается (если вы не используете отдельный процесс или базу данных для хранения состояния сеанса) и работает против механизмов, которые ASP.NET имеет для управления аутентификацией пользователя. – HackedByChinese