簡體   English   中英

ASP.NET Forms身份驗證超時

[英]ASP.NET Forms Authentication timeout

這可能是一個非常簡單的問題,但在幾個小時后試圖理解它在ASP.NET 4.0上是如何工作的,我仍然不知道。

我正在使用表單身份驗證。 我有一個登錄頁面,上面有登錄控件。

這是用戶登錄時所需要的:

A-用戶應該保持記錄狀態,直到對超時設置不做任何操作。 如果他們重新加載頁面,則超時必須重新開始倒計時。

B-如果他們點擊“記住我”檢查他們應該保持聯系,直到他們退出,無論他們是關閉瀏覽器還是重新啟動計算機。

我遇到的問題是他們登錄時我的計算機上沒有看到任何cookie:

  1. 餅干在哪里? 是內存cookie嗎?
  2. 如果會話到期會發生什么? 除非超時完成,否則我想讓它們保持記錄狀態。
  3. 如果應用程序池被回收會發生什么?

另外我還有另一個問題:當他們點擊“記住我”檢查(案例B)時,我希望他們記錄,直到他們點擊退出按鈕。 這次我確實看到了一個cookie,但看起來它們只是為了超時而保持連接...所以記住我與否的區別是什么......

我想完全分開身份驗證和會話。 我希望身份驗證由cookie控制,如果接近不是很糟糕。

謝謝你的幫助。

處理非永久性滑動到期票

表單身份驗證使用內存中的cookie作為故障單,除非您使其持久化(例如, 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請求,客戶端超時和票證(以cookie的形式)都將被更新,您的用戶應該沒問題。

但是,如果用戶活動不會導致更新FormsAuth故障單,則用戶下次請求新頁面時(通過導航或通過AJAX)似乎將被注銷。 在這種情況下,當用戶活動發生時,您需要通過AJAX調用(例如,自定義處理程序,MVC操作等)來“ping”您的Web應用程序,以使您的FormsAuth票證保持最新。 請注意,在ping服務器以保持最新時需要小心,因為您不希望服務器充滿請求,例如,移動光標或單擊事物。 除了初始頁面加載和AJAX請求之外,這里還添加了上面的init腳本,它將resetRedirect添加到文檔的鼠標單擊上。

$(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中存儲角色,因此您無需再次查找它們。 是的,這是可能的,但有一些考慮因素。

  1. 您應該限制您在故障單中放入的數據量,因為cookie只能是這么大
  2. 您應該考慮是否應該在客戶端緩存角色。

詳細說明#2:

您不應該隱含地信任您從用戶那里收到的聲明。 例如,如果用戶登錄並且是管理員,並且檢查“記住我”從而接收持久的長期票證,則他們將永遠是管理員(或者直到該cookie過期或被刪除)。 如果有人在數據庫中將其從該角色中刪除,則應用程序仍然認為如果他們擁有舊票證,則他們是管理員。 因此,您最好每次獲取用戶的角色,但在應用程序實例中緩存角色一段時間以最大限度地減少數據庫工作。

從技術上講,這也是機票本身的問題。 同樣,您不應該相信這只是因為他們擁有該帳戶仍然有效的有效票證。 您可以使用與角色類似的邏輯:通過查詢實際數據庫檢查故障單引用的用戶是否仍然存在並且是否有效(它沒有被鎖定,禁用或刪除),只需將數據庫緩存緩存一段時間時間來提高績效。 這就是我在我的應用程序中所做的事情,其中​​票證被視為身份聲明(類似地,用戶名/密碼是另一種類型的聲明)。 這是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);
  }
}

對於A,您需要將會話超時變量設置為您希望用戶保持登錄的時間長度。超時屬性指定分配給應用程序的Session對象的超時時間(以分鍾為單位)。 如果用戶在超時期限內沒有刷新或請求頁面,則會話結束。

對於B部分,我建議將該值存儲在會話變量(或cookie但不駐留在服務器上)中,並在global.asax文件中的Session_End事件中檢查該值。 如果已設置,則續訂會話。

當瀏覽器關閉時,Session_End事件不會觸發,當服務器在特定時間段內沒有收到用戶的請求時(默認為20分鍾),它會觸發。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM