在 MVC Identity (2.0.1) 中 regenerateIdentity / validateInterval 持續時間後忽略 ExpireTimeSpan
一整天都在為這個撓頭。我正在嘗試在 MVC Identity 2.0.1 中設置“非常長”的登錄會話。(30天)。
我使用以下 cookie 啟動:
app.UseCookieAuthentication(new CookieAuthenticationOptions { SlidingExpiration = true, ExpireTimeSpan = System.TimeSpan.FromDays(30), AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, LoginPath = new PathString("/My/Login"), CookieName = "MyLoginCookie", Provider = new CookieAuthenticationProvider { OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>( validateInterval: TimeSpan.FromMinutes(30), regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager)) } });總的來說,效果很好。cookie 設置為 30 天,一切看起來都不錯。
如果我關閉瀏覽器並在“validateInterval”持續時間過去(此處為 30 分鐘)後返回,我仍然登錄,但是 cookie 現在僅作為“會話”重新發布(仍然是正確的 cookie 名稱)!30天的有效期已經過去了。
如果我現在再次關閉瀏覽器/重新打開,我將不再登錄。
我已經測試了刪除“提供者”並且所有工作都按預期進行,我可以在幾個小時後回來,我仍然可以正常登錄。我讀到最好的做法是使用郵票重新驗證,所以我不確定如何繼續。
當
SecurityStampValidator觸發regenerateIdentity回調時,目前經過身份驗證的使用者將使用非持久登錄重新登錄。這是硬編碼的,我不相信有任何方法可以直接控制它。因此,登錄會話將僅持續到您在重新生成身份時正在執行的瀏覽器會話結束。這是一種使登錄持久化的方法,即使跨身份重新生成操作也是如此。此描述基於使用 Visual Studio MVC ASP.NET Web 項目模板。
首先,我們需要有一種方法來跟踪登錄會話在不同的 HTTP 請求中是持久的這一事實。這可以通過向使用者身份添加“IsPersistent”聲明來完成。以下擴展方法顯示了一種方法。
public static class ClaimsIdentityExtensions { private const string PersistentLoginClaimType = "PersistentLogin"; public static bool GetIsPersistent(this System.Security.Claims.ClaimsIdentity identity) { return identity.Claims.FirstOrDefault(c => c.Type == PersistentLoginClaimType) != null; } public static void SetIsPersistent(this System.Security.Claims.ClaimsIdentity identity, bool isPersistent) { var claim = identity.Claims.FirstOrDefault(c => c.Type == PersistentLoginClaimType); if (isPersistent) { if (claim == null) { identity.AddClaim(new System.Security.Claims.Claim(PersistentLoginClaimType, Boolean.TrueString)); } } else if (claim != null) { identity.RemoveClaim(claim); } } }接下來,當使用者登錄請求持久會話時,我們需要提出“IsPersistent”聲明。例如,您的
ApplicationUser類可能有一個GenerateUserIdentityAsync方法,該方法可以更新為採用isPersistent如下標誌參數,以便在需要時提出此類聲明:public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager, bool isPersistent) { var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie); userIdentity.SetIsPersistent(isPersistent); return userIdentity; }現在,任何呼叫者
ApplicationUser.GenerateUserIdentityAsync都需要傳入isPersistent標誌。例如,對GenerateUserIdentityAsyncin的呼叫AccountController.SignInAsync將從AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, await user.GenerateUserIdentityAsync(UserManager));到
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, await user.GenerateUserIdentityAsync(UserManager, isPersistent));最後,該方法
CookieAuthenticationProvider.OnValidateIdentity中使用的委託Startup.ConfigureAuth需要注意保留跨身份重新生成操作的持久性細節。預設委託如下所示:OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>( validateInterval: TimeSpan.FromMinutes(20), regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))這可以更改為:
OnValidateIdentity = async (context) => { await SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>( validateInterval: TimeSpan.FromMinutes(20), // Note that if identity is regenerated in the same HTTP request as a logoff attempt, // the logoff attempt will have no effect and the user will remain logged in. // See https://aspnetidentity.codeplex.com/workitem/1962 regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager, context.Identity.GetIsPersistent()) )(context); // If identity was regenerated by the stamp validator, // AuthenticationResponseGrant.Properties.IsPersistent will default to false, leading // to a non-persistent login session. If the validated identity made a claim of being // persistent, set the IsPersistent flag to true so the application cookie won't expire // at the end of the browser session. var newResponseGrant = context.OwinContext.Authentication.AuthenticationResponseGrant; if (newResponseGrant != null) { newResponseGrant.Properties.IsPersistent = context.Identity.GetIsPersistent(); } }