关于ASP.NET表单身份validation和会话的滑动到期

我们有一个使用本机表单身份validation和会话function的ASP.NET 4.5 WebForms应用程序。 滑动到期时两者都超时20分钟。

想象一下以下场景。 用户已经在我们的应用程序中工作了一段时间,然后继续做其他事情,让我们的应用程序闲置20分钟。 然后,用户返回我们的应用程序以编写报告。 但是,当用户尝试保存时,他/她将使用登录屏幕进行处理,并且报告将丢失。

显然,这是不需要的。 我们希望在身份validation或会话过期时将浏览器重定向到登录页面,而不是这种情况。 为了实现这一点,我们构建了一个Web Api服务,可以调用它来检查是否是这种情况。

public class SessionIsActiveController : ApiController { ///  /// Gets a value defining whether the session that belongs with the current HTTP request is still active or not. ///  /// 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; } } 

客户端每10秒调用此Web Api服务,以检查身份validation或会话是否已过期。 如果是,则脚本将浏览器重定向到登录页面。 这就像一个魅力。

但是,调用此服务会触发身份validation和会话的滑动到期。 因此,基本上,创建永无止境的身份validation和会话。 我在服务开始时设置了一个断点,以检查它是否是我们自己的一个触发它的函数。 但事实并非如此,它似乎发生在ASP.NET的更深处,在服务执行之前。

  1. 有没有办法禁用ASP.NET的身份validation和会话滑动到期特定请求的触发?
  2. 如果没有,那么解决这种情况的最佳做法是什么?

  1. 这似乎是不可能的。 启用滑动过期后,始终会触发它。 如果有一种方法可以访问会话而不扩展它,我们无法找到它。

  2. 那么如何解决这个问题呢? 我们提出了以下替代方案,以解决问题中最初提出的解决方案。 这个实际上更有效,因为它不会每隔x秒使用Web服务回家。

因此,我们希望有一种方法可以知道ASP.NET的表单身份validation或会话何时过期,因此我们可以主动注销用户。 每个页面上的一个简单的javascript计时器(由Khalid Abuhakmeh 提出 )是不够的,因为用户可能同时在多个浏览器窗口/选项卡中使用该应用程序。

我们为使此问题更简单而做出的第一个决定是使会话的到期时间比表单身份validation的到期时间长几分钟。 这样,会话将永远在表单身份validation之前到期。 如果下次用户尝试登录时存在挥之不去的旧会话,我们会放弃它以强制使用新的会话。

好的,现在我们只需要考虑表单身份validation到期。

接下来,我们决定禁用表单身份validation的自动滑动过期(在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); } } 

我们从应用程序的BasePage类中的OnPreRenderComplete事件中调用此方法,每个其他页面都从该类inheritance。 它与正常的滑动到期function完全相同,但我们有机会做一些额外的事情; 比如调用我们的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来表示正确的表单身份validation到期时间。 毕竟,cookie具有浏览器范围。 现在唯一剩下的是用于validationcookie值的javascript函数。

 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插件来检索cookie。)

将此function放在一个间隔中,用户将在其表单身份validation过期时注销。 Voilà:-)这个实现的额外好处是你现在可以控制表单身份validation的到期何时扩展。 如果您想要一堆不延长过期时间的Web服务,请不要为它们调用RenewAuthenticationTicket方法。

如果您有任何要添加的内容,请发表评论!

这一切都可以在客户端解决,而无需返回服务器。

在JavaScript中执行此操作。

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

每次刷新页面时,超时时间设置为20分钟。 这可确保用户需要在超时发生之前完成所有工作。 很多站点都使用这种方法,它可以帮助您避免执行不必要的服务器请求。

您的网站function应该可以在没有JavaScript的情况下运行,或者您只需将一个问 我也解决了这个问题,这里是如何解决的:

当您对自己进行身份validation时,会创建会话cookie,默认生命周期为20分钟。 当此过期时,用户将被注销。

饼干图像

当用户在登录表单中选择“记住我”时,会在客户端和数据库中创建其他持久性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数据是否与数据库数据匹配,因为可以更改存储在客户端的任何内容。 如果您拥有具有访问权限的付费服务,则需要检查用户是否仍具有这些权限,然后您可以重新创建会话。