ASP.NET_SessionId + OWIN Cookies 不發送到瀏覽器
我在使用 Owin cookie 身份驗證時遇到了一個奇怪的問題。
當我啟動 IIS 伺服器身份驗證時,在 IE/Firefox 和 Chrome 上執行良好。
我開始使用身份驗證進行一些測試並在不同的平台上登錄,但我遇到了一個奇怪的錯誤。偶爾 Owin 框架/IIS 只是不向瀏覽器發送任何 cookie。我將輸入正確的使用者名和密碼,程式碼執行但根本沒有 cookie 傳送到瀏覽器。如果我重新啟動伺服器,它就會開始工作,那麼在某個時候我會嘗試登錄,然後 cookie 會再次停止傳遞。單步執行程式碼不會執行任何操作,也不會引發錯誤。
app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationMode = AuthenticationMode.Active, CookieHttpOnly = true, AuthenticationType = "ABC", LoginPath = new PathString("/Account/Login"), CookiePath = "/", CookieName = "ABC", Provider = new CookieAuthenticationProvider { OnApplyRedirect = ctx => { if (!IsAjaxRequest(ctx.Request)) { ctx.Response.Redirect(ctx.RedirectUri); } } } });在我的登錄過程中,我有以下程式碼:
IAuthenticationManager authenticationManager = HttpContext.Current.GetOwinContext().Authentication; authenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie); var authentication = HttpContext.Current.GetOwinContext().Authentication; var identity = new ClaimsIdentity("ABC"); identity.AddClaim(new Claim(ClaimTypes.Name, user.Username)); identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.User_ID.ToString())); identity.AddClaim(new Claim(ClaimTypes.Role, role.myRole.ToString())); authentication.AuthenticationResponseGrant = new AuthenticationResponseGrant(identity, new AuthenticationProperties() { IsPersistent = isPersistent }); authenticationManager.SignIn(new AuthenticationProperties() {IsPersistent = isPersistent}, identity);**更新1:**問題的一個原因似乎是當我將項目添加到會話時問題開始了。添加一些簡單
Session.Content["ABC"]= 123的東西似乎會產生問題。我可以做的如下: 1)(Chrome)當我登錄時,我得到 ASP.NET_SessionId + 我的身份驗證 cookie。2)我轉到一個設置 session.contents 的頁面… 3)打開一個新瀏覽器(Firefox)並嘗試登錄,它沒有收到 ASP.NET_SessionId 也沒有獲得身份驗證 Cookie 4)雖然第一個瀏覽器有它繼續工作的 ASP.NET_SessionId。在我刪除此 cookie 的那一刻,它與我在 ip 地址 (10.xxx) 和 localhost 上工作的所有其他瀏覽器存在相同的問題。
更新 2:
ASPNET_SessionId在使用 OWIN 進行身份驗證之前,在我的 login_load 頁面上強制創建第一個。1)在我使用 OWIN 進行身份驗證之前,我
Session.Content在登錄頁面上創建一個隨機值以啟動 ASP.NET_SessionId 2)然後我進行身份驗證並進行進一步的會話 3)其他瀏覽器現在似乎可以工作這很奇怪。我只能得出結論,這與 ASP 和 OWIN 認為它們位於不同的域或類似的東西有關。
更新 3 - 兩者之間的奇怪行為。
發現了其他奇怪的行為 - Owin 和 ASP 會話的超時是不同的。我所看到的是,通過某種機制,我的 Owin 會話比我的 ASP 會話存活的時間更長。所以登錄時:1.)我有一個基於 cookie 的身份驗證會話 2.)我設置了一些會話變數
我的會話變數(2)在 owin cookie 會話變數強制重新登錄之前“死亡”,這會導致整個應用程序出現意外行為。(此人已登錄但並未真正登錄)
更新 3B
經過一番探勘,我在一個頁面上看到了一些評論,說“表單”身份驗證超時和會話超時需要匹配。我通常認為兩者是同步的,但無論出於何種原因,兩者都不同步。
變通辦法摘要
1)在認證之前總是先創建一個會話。基本上在啟動應用程序時創建會話
Session["Workaround"] = 0;
- [實驗性] 如果您堅持使用 cookie,請確保您的 OWIN 超時/長度比您的 web.config 中的 sessionTimeout 長(測試中)
我遇到了同樣的問題,並將原因追溯到 OWIN ASP.NET 託管實現。我會說這是一個錯誤。
一些背景
我的發現基於這些程序集版本:
- Microsoft.Owin,版本=2.0.2.0,文化=中性,PublicKeyToken=31bf3856ad364e35
- Microsoft.Owin.Host.SystemWeb,版本=2.0.2.0,文化=中性,PublicKeyToken=31bf3856ad364e35
- System.Web,版本=4.0.0.0,文化=中性,PublicKeyToken=b03f5f7f11d50a3a
OWIN 使用它自己的抽象來處理響應 Cookie ( Microsoft.Owin.ResponseCookieCollection )。此實現直接包裝響應頭集合併相應地更新Set-Cookie頭。OWIN ASP.NET 主機 ( Microsoft.Owin.Host.SystemWeb ) 只是包裝System.Web.HttpResponse和它的標題集合。所以當通過 OWIN 創建新的 cookie 時,響應Set-Cookie頭會直接改變。
但是 ASP.NET 也使用它自己的抽象來處理響應 Cookie。這作為System.Web.HttpResponse.Cookies屬性向我們公開,並由密封類System.Web.HttpCookieCollection實現。此實現不直接包裝響應Set-Cookie標頭,而是使用一些優化和少量內部通知來表明它已更改狀態以響應對象。
然後在請求生命週期的後期,測試HttpCookieCollection更改的狀態(System.Web.HttpResponse.GenerateResponseHeadersForCookies())並將 cookie 序列化為Set-Cookie標頭。如果此集合處於某種特定狀態,則首先清除整個 Set-Cookie 標頭並從儲存在集合中的 cookie 重新創建。
ASP.NET 會話實現使用System.Web.HttpResponse.Cookies屬性來儲存它的 ASP.NET_SessionId cookie。此外,通過名為 s_sessionEverSet 的靜態屬性實現的 ASP.NET 會話狀態模組 ( System.Web.SessionState.SessionStateModule )中也有一些基本的優化,這是不言自明的。如果您曾經在應用程序中將某些內容儲存到會話狀態,則此模組將為每個請求做更多的工作。
回到我們的登錄問題
通過所有這些片段,您的場景可以得到解釋。
案例 1 - 會話從未設置
System.Web.SessionState.SessionStateModule, s_sessionEverSet 屬性為假。會話狀態模組不會生成會話 ID,並且System.Web.HttpResponse.Cookies集合狀態未檢測為已更改。在這種情況下,OWIN cookie 會正確發送到瀏覽器並且登錄正常。
案例 2 - 會話在應用程序的某處使用,但不是在使用者嘗試進行身份驗證之前
System.Web.SessionState.SessionStateModule, s_sessionEverSet 屬性為真。會話 ID 由SessionStateModule生成,ASP.NET_SessionId 被添加到System.Web.HttpResponse.Cookies集合中,但在請求生命週期的後期被刪除,因為使用者的會話實際上是空的。在這種情況下, System.Web.HttpResponse.Cookies集合狀態被檢測為已更改,並且在將cookie 序列化為標頭值之前首先清除Set-Cookie標頭。
在這種情況下,OWIN 響應 cookie “失去”並且使用者未通過身份驗證並被重定向回登錄頁面。
案例 3 - 在使用者嘗試進行身份驗證之前使用會話
System.Web.SessionState.SessionStateModule, s_sessionEverSet 屬性為真。Session Id 由SessionStateModule生成,ASP.NET_SessionId 添加到System.Web.HttpResponse.Cookies中。由於System.Web.HttpCookieCollection和System.Web.HttpResponse.GenerateResponseHeadersForCookies()的內部優化, Set-Cookie 標頭不會首先清除,而只會更新。
在這種情況下,OWIN 身份驗證 cookie 和 ASP.NET_SessionId cookie 都會作為響應發送,並且登錄工作正常。
cookie 更普遍的問題
如您所見,問題更為普遍,不僅限於 ASP.NET 會話。如果您通過Microsoft.Owin.Host.SystemWeb託管 OWIN,並且您/某物直接使用System.Web.HttpResponse.Cookies集合,您將面臨風險。
例如,這可行,並且兩個 cookie 都正確發送到瀏覽器…
public ActionResult Index() { HttpContext.GetOwinContext() .Response.Cookies.Append("OwinCookie", "SomeValue"); HttpContext.Response.Cookies["ASPCookie"].Value = "SomeValue"; return View(); }但這並沒有,並且 OwinCookie 被“失去”了……
public ActionResult Index() { HttpContext.GetOwinContext() .Response.Cookies.Append("OwinCookie", "SomeValue"); HttpContext.Response.Cookies["ASPCookie"].Value = "SomeValue"; HttpContext.Response.Cookies.Remove("ASPCookie"); return View(); }均從 VS2013、IISExpress 和預設 MVC 項目模板測試。