ASP.NET_SessionId + OWIN Cookie 不会发送到浏览器

标签 asp.net asp.net-identity owin katana

我在使用 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 添加项目时,问题就开始了。添加一些简单的东西,比如 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.x.x.x) 和 localhost。

更新 2:强制创建 ASPNET_SessionId在使用 OWIN 进行身份验证之前,首先在我的 login_load 页面上。

1) 在使用 OWIN 进行身份验证之前,我会随机生成一个 Session.Content我的登录页面上的值以启动 ASP.NET_SessionId
2)然后我进行身份验证并进行进一步的 session
3)其他浏览器现在似乎可以工作

这很奇怪。我只能得出结论,这与 ASP 和 OWIN 认为它们在不同的域或类似的东西有关。

更新 3 - 两者之间的奇怪行为。

发现的其他奇怪行为 - Owin 和 ASP session 的超时不同。我看到的是,通过某种机制,我的 Owin session 比我的 ASP session 保持更长时间。所以在登录时:
1.) 我有一个基于 cookied 的身份验证 session
2.) 我设置了一些 session 变量

我的 session 变量 (2) 在 owin cookie session 变量强制重新登录之前“死亡”,这会导致整个应用程序出现意外行为。 (此人已登录但并未真正登录)

更新 3B

经过一番挖掘,我在页面上看到一些评论,说“表单”身份验证超时和 session 超时需要匹配。我认为通常两者是同步的,但无论出于何种原因,两者都不同步。

解决方法摘要

1)在认证之前总是先创建一个 session 。基本上在启动应用程序时创建 session Session["Workaround"] = 0;
2) [实验] 如果您坚持 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)。此实现直接包装响应 header 集合并相应地更新 Set-Cookie header 。 OWIN ASP.NET 主机 (Microsoft.Owin.Host.SystemWeb) 只是包装 System.Web.HttpResponse 和它的 header 集合。所以当通过 OWIN 创建新的 cookie 时,直接更改响应 Set-Cookie header 。

    但是 ASP.NET 也使用它自己的抽象来处理响应 Cookie。这作为 System.Web.HttpResponse.Cookies 属性公开给我们,并由密封类 System.Web.HttpCookieCollection 实现。此实现不直接包装响应 Set-Cookie header ,而是使用一些优化和少量内部通知来显示它已更改的状态到响应对象。

    然后在请求生命周期的后期,测试 HttpCookieCollection 更改状态 (System.Web.HttpResponse.GenerateResponseHeadersForCookies()) 并将 cookie 序列化为 Set-Cookie header 。如果此集合处于某种特定状态,则首先清除整个 Set-Cookie header ,并从存储在集合中的 cookie 中重新创建。

    ASP.NET session 实现使用 System.Web.HttpResponse.Cookies 属性来存储它的 ASP.NET_SessionId cookie。在 ASP.NET session 状态模块 (System.Web.SessionState.SessionStateModule) 中也有一些基本优化,通过名为 s_sessionEverSet 的静态属性实现,这是不言自明的。如果你曾经在你的应用程序中存储一些东西到 session 状态,这个模块将为每个请求做更多的工作。

    回到我们的登录问题

    有了所有这些片段,您的场景就可以得到解释。

    案例 1 - session 从未设置

    System.Web.SessionState.SessionStateModule,s_sessionEverSet 属性为 false。 session 状态模块不生成 session ID,System.Web.HttpResponse.Cookies 收集状态为 未检测到更改 .在这种情况下,OWIN cookie 被正确发送到浏览器并且登录正常工作。

    案例 2 - session 在应用程序中的某处使用,但在用户尝试进行身份验证之前未使用

    System.Web.SessionState.SessionStateModule,s_sessionEverSet 属性为真。 session ID 由 SessionStateModule 生成,ASP.NET_SessionId 被添加到 System.Web.HttpResponse.Cookies 集合中,但由于用户的 session 实际上是空的,因此它在请求生命周期的后期被删除。在这种情况下 System.Web.HttpResponse.Cookies 集合状态是 检测到已更改 在将 cookie 序列化为 header 值之前,首先清除 Set-Cookie header 。

    在这种情况下,OWIN 响应 cookie 将“丢失”并且用户未通过身份验证并被重定向回登录页面。

    案例 3 - 在用户尝试进行身份验证之前使用 session

    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 header 未首先清除 但只是更新。

    在这种情况下,OWIN 身份验证 cookie 和 ASP.NET_SessionId cookie 都会作为响应发送并且登录有效。

    cookie 的更普遍问题

    如您所见,该问题更为普遍,不仅限于 ASP.NET session 。如果您通过 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 项目模板进行测试。

    关于ASP.NET_SessionId + OWIN Cookie 不会发送到浏览器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20737578/

    相关文章:

    c# - 如何修复 "IDX20804: Unable to retrieve document from: ' [PII 在 C# 中隐藏 ]'"错误

    asp.net - 如何获取 Sitecore 同级项目数组?

    asp.net-mvc - 从 ASP.NET Identity 2.x 中的角色中删除用户

    c# - 对 ASP.NET 实体和身份使用 1 个连接字符串

    c# - 如何在WebApi上实现AuthorizationContext属性?

    ASP.NET 与 Instagram 身份验证

    c# - 使用 MultipartFormDataStreamProvider 和 ReadAsMultipartAsync

    c# - 参数个数错误(参数化查询)

    asp.net - 数据列表中的行分隔符

    signalr - OwinMiddleware 和 IOwinContext 等 Microsoft.Owin 类型是否与其他 Owin 服务器不兼容?