Asp.net

ASP.NET_SessionId + OWIN Cookies 不發送到瀏覽器

  • December 23, 2013

我在使用 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;

  1. [實驗性] 如果您堅持使用 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.HttpCookieCollectionSystem.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 項目模板測試。

引用自:https://stackoverflow.com/questions/20737578