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

2019-05-08 22:46发布

我有使用Owin cookie认证一个奇怪的问题。

当我开始我的IIS服务器的认证工作在IE / Firefox和Chrome完美的罚款。

我开始做一些测试与认证,并在不同平台上登录,我想出了一个奇怪的错误。 偶尔的Owin框架/ IIS只是不发送任何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)(铬)当我登录,我得到ASP.NET_SessionId +我的身份验证cookie。 2)我去那台session.contents ... 3)打开一个新的浏览器(Firefox),并尝试登录,并没有收到ASP.NET_SessionId也没有得到验证cookie 4)虽然第一个浏览器页面有ASP.NET_SessionId它继续工作。 我删除此Cookie的分钟它具有相同的问题,因为我的全部IP地址(10.xxx)和本地主机工作的其他浏览器。

更新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)我设置一些会话变量

在owin cookie的会话变量力量之前我的会话变量(2)“死”重新登录,这将导致在我整个应用程序的意外行为。 (人已经登录,但并没有真正登录)

更新3B

一些挖后,我看到一个页面上说,“形式”验证超时和会话超时需要匹配一些意见。 我通常想这两者是同步的,但无论什么原因,两者不同步。

解决方法汇总

1)始终在认证之前先创建一个会话。 当启动应用程序基本上创建会话Session["Workaround"] = 0;

2)[实验]如果你坚持下去饼干确保您OWIN超时/长度比你sessionTimeout长在你的web.config(测试)

Answer 1:

我也遇到同样的问题,追查原因OWIN ASP.NET主机实现。 我会说这是一个错误。

一些背景知识

我的发现是基于对这些组件的版本:

  • Microsoft.Owin,版本= 2.0.2.0,文化=中性公钥= 31bf3856ad364e35
  • Microsoft.Owin.Host.SystemWeb,版本= 2.0.2.0,文化=中性公钥= 31bf3856ad364e35
  • System.Web程序,版本= 4.0.0.0,文化=中立,公钥= b03f5f7f11d50a3a

OWIN使用它自己的抽象与响应饼干(Microsoft.Owin.ResponseCookieCollection)工作。 此实现直接包装响应头采集并相应地更新设置Cookie头。 OWIN ASP.NET主机(Microsoft.Owin.Host.SystemWeb)只是包装System.Web.HttpResponse和它的头集合。 所以,当通过OWIN创建新的cookie,响应设置Cookie头直接更改。

但是,ASP.NET还使用了它自己的抽象与响应饼干工作。 这是暴露在我们的System.Web.HttpResponse.Cookies性能和密封类System.Web.HttpCookieCollection实现。 此实现不直接响应包设置Cookie头,但使用一些优化和内部通知的少数体现它改变了状态,以响应对象。

然后是在请求寿命末期的点处改变HttpCookieCollection状态进行测试(System.Web.HttpResponse.GenerateResponseHeadersForCookies())和Cookie是序列化到set-cookie头。 如果该集合是在一些特定的状态,整个的Set-Cookie头先清除从存储在收集饼干重建。

ASP.NET会话实现使用System.Web.HttpResponse.Cookies属性来存储它的ASP.NET_SessionId饼干。 也有是通过名为s_sessionEverSet静态属性是相当自我解释实现ASP.NET会话状态模块(System.Web.SessionState.SessionStateModule)一些基本的优化。 如果你存储的东西会话状态在您的应用程序,该模块会做更多的工作为每个请求。


回到我们的登录问题

随着这些作品的情况下可以解释。

案例1 -会议从未设置过

System.Web.SessionState.SessionStateModule,s_sessionEverSet属性为false。 为改变未检测到任何会话ID是由会话状态模块和System.Web.HttpResponse.Cookies集合状态产生。 在这种情况下OWIN饼干被正确地发送到浏览器和登录作品。

案例2 -会议的某处使用应用程序,而不是之前的用户尝试认证

System.Web.SessionState.SessionStateModule,s_sessionEverSet属性为true。 会话ID是由SessionStateModule产生,ASP.NET_SessionId添加到收藏System.Web.HttpResponse.Cookies但它在请求生命周期后移除用户的会话实际上是空的。 在这种情况下System.Web.HttpResponse.Cookies采集状态变化Set-Cookie头先清除之前的cookie被序列化到标头值进行检测

在这种情况下OWIN响应饼干“丢失”和用户没有被授权,被重定向回登录页。

案例3 -用户尝试认证之前会话使用

System.Web.SessionState.SessionStateModule,s_sessionEverSet属性为true。 会话ID是由SessionStateModule产生,ASP.NET_SessionId添加到System.Web.HttpResponse.Cookies。 由于System.Web.HttpCookieCollectionSystem.Web.HttpResponse.GenerateResponseHeadersForCookies()内部优化设置Cookie标头不是第一次清除 ,但只更新。

在这种情况下,两个OWIN身份验证Cookie和ASP.NET_SessionId饼干响应和登录作品被发送。


与饼干更普遍的问题

正如你所看到的问题是更普遍的,而不是仅限于ASP.NET会话。 如果您是通过Microsoft.Owin.Host.SystemWeb你托管OWIN /什么是直接用System.Web.HttpResponse.Cookies收集你处于危险之中。

例如这个工程和饼干都正确地发送到浏览器...

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项目模板测试。



Answer 2:

与@TomasDolezal伟大的分析入手,我看了一下这两个Owin和System.Web程序源。

问题是,有System.Web程序的cookie信息,它自己的主源,这是不是在Set-Cookie头。 Owin只知道有关Set-Cookie头。 一种解决方法是确保由Owin设置任何cookie中也设置HttpContext.Current.Response.Cookies集合。

我已经做了一个小的中间件( 源 , 的NuGet即正是如此),其意在被该cookie中间件登记以上立即放置。

app.UseKentorOwinCookieSaver();

app.UseCookieAuthentication(new CookieAuthenticationOptions());


Answer 3:

总之,.NET cookie管理器将战胜OWIN cookie管理器,并覆盖OWIN层上设置Cookie。 解决方法是使用SystemWebCookieManager类,在武士刀在此项目的解决方案提供 。 您需要使用这个类或一个类似于它,这将迫使OWIN使用.NET cookie管理器来所以没有不一致之处

public class SystemWebCookieManager : ICookieManager
{
    public string GetRequestCookie(IOwinContext context, string key)
    {
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }

        var webContext = context.Get<HttpContextBase>(typeof(HttpContextBase).FullName);
        var cookie = webContext.Request.Cookies[key];
        return cookie == null ? null : cookie.Value;
    }

    public void AppendResponseCookie(IOwinContext context, string key, string value, CookieOptions options)
    {
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }
        if (options == null)
        {
            throw new ArgumentNullException("options");
        }

        var webContext = context.Get<HttpContextBase>(typeof(HttpContextBase).FullName);

        bool domainHasValue = !string.IsNullOrEmpty(options.Domain);
        bool pathHasValue = !string.IsNullOrEmpty(options.Path);
        bool expiresHasValue = options.Expires.HasValue;

        var cookie = new HttpCookie(key, value);
        if (domainHasValue)
        {
            cookie.Domain = options.Domain;
        }
        if (pathHasValue)
        {
            cookie.Path = options.Path;
        }
        if (expiresHasValue)
        {
            cookie.Expires = options.Expires.Value;
        }
        if (options.Secure)
        {
            cookie.Secure = true;
        }
        if (options.HttpOnly)
        {
            cookie.HttpOnly = true;
        }

        webContext.Response.AppendCookie(cookie);
    }

    public void DeleteCookie(IOwinContext context, string key, CookieOptions options)
    {
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }
        if (options == null)
        {
            throw new ArgumentNullException("options");
        }

        AppendResponseCookie(
            context,
            key,
            string.Empty,
            new CookieOptions
            {
                Path = options.Path,
                Domain = options.Domain,
                Expires = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc),
            });
    }
}

在应用程序启动时,只是为它分配当您创建OWIN依赖关系:

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    ...
    CookieManager = new SystemWebCookieManager()
    ...
});

类似的答案已经在这里提供,但它并不包括所有解决问题所需的代码库的,所以我认为有必要在此补充,因为外部链接武士刀项目可能会下降,这应得到充分记载这里作为解决方案,以及。



Answer 4:

武士刀团队回答的问题,托马斯Dolezar提出,并张贴有关解决方法的文档 :

解决方法分为两类。 一个是因此它避免了使用Response.Cookies集合并覆盖OWIN饼干重新配置的System.Web。 另一种方法是让他们直接写饼干的System.Web的Response.Cookies集合重新配置受影响的OWIN组件。

  • 确保会话认证之前建立:System.Web程序和武士刀饼干之间的冲突是每个请求,所以有可能为应用程序之前建立的一些要求该会话的认证流程。 这应该是很容易做到,当用户第一次到达,但它可能是以后很难保证当会话或身份验证Cookie过期和/或需要刷新。
  • 禁用SessionStateModule - 如果应用程序不依赖会话信息,但会话模块仍然设置导致上述冲突cookie,那么你可以考虑禁用会话状态模块。
  • 重新配置CookieAuthenticationMiddleware直接写的System.Web的Cookie集合。
app.UseCookieAuthentication(new CookieAuthenticationOptions
                                {
                                    // ...
                                    CookieManager = new SystemWebCookieManager()
                                });

请参阅从文件(以上链接)SystemWebCookieManager实施

更多信息点击这里

编辑

下面的步骤,我们采取了解决这一问题。 1.双方和2解决了这个问题还分别,但我们决定以防万一申请两个:

1.使用SystemWebCookieManager

2.设置会话变量:

protected override void Initialize(RequestContext requestContext)
{
    base.Initialize(requestContext);

    // See http://stackoverflow.com/questions/20737578/asp-net-sessionid-owin-cookies-do-not-send-to-browser/
    requestContext.HttpContext.Session["FixEternalRedirectLoop"] = 1;
}

(边注:以上初始化方法是修复的合理位置,因为base.Initialize使会话中可用但是,修复也可后来因为在的OpenID有第一匿名请求应用,然后重定向到OpenID提供商,然后再到该应用。重定向返回到应用程序后,同时固定第一匿名请求期间设置会话变量已经由此之前的任何重定向回甚至发生解决了这个问题会发生问题)

编辑2

从复制粘贴卡塔纳项目 2016年5月14日:

补充一点:

app.UseCookieAuthentication(new CookieAuthenticationOptions
                                {
                                    // ...
                                    CookieManager = new SystemWebCookieManager()
                                });

...还有这个:

public class SystemWebCookieManager : ICookieManager
{
    public string GetRequestCookie(IOwinContext context, string key)
    {
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }

        var webContext = context.Get<HttpContextBase>(typeof(HttpContextBase).FullName);
        var cookie = webContext.Request.Cookies[key];
        return cookie == null ? null : cookie.Value;
    }

    public void AppendResponseCookie(IOwinContext context, string key, string value, CookieOptions options)
    {
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }
        if (options == null)
        {
            throw new ArgumentNullException("options");
        }

        var webContext = context.Get<HttpContextBase>(typeof(HttpContextBase).FullName);

        bool domainHasValue = !string.IsNullOrEmpty(options.Domain);
        bool pathHasValue = !string.IsNullOrEmpty(options.Path);
        bool expiresHasValue = options.Expires.HasValue;

        var cookie = new HttpCookie(key, value);
        if (domainHasValue)
        {
            cookie.Domain = options.Domain;
        }
        if (pathHasValue)
        {
            cookie.Path = options.Path;
        }
        if (expiresHasValue)
        {
            cookie.Expires = options.Expires.Value;
        }
        if (options.Secure)
        {
            cookie.Secure = true;
        }
        if (options.HttpOnly)
        {
            cookie.HttpOnly = true;
        }

        webContext.Response.AppendCookie(cookie);
    }

    public void DeleteCookie(IOwinContext context, string key, CookieOptions options)
    {
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }
        if (options == null)
        {
            throw new ArgumentNullException("options");
        }

        AppendResponseCookie(
            context,
            key,
            string.Empty,
            new CookieOptions
            {
                Path = options.Path,
                Domain = options.Domain,
                Expires = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc),
            });
    }
}


Answer 5:

如果要设置在OWIN中间件自己的cookies,然后用OnSendingHeaders似乎避开这个问题。

例如,使用下面的代码owinResponseCookie2将被设置,即使owinResponseCookie1不是:

private void SetCookies()
{
    var owinContext = HttpContext.GetOwinContext();
    var owinResponse = owinContext.Response;

    owinResponse.Cookies.Append("owinResponseCookie1", "value1");

    owinResponse.OnSendingHeaders(state =>
    {
        owinResponse.Cookies.Append("owinResponseCookie2", "value2");
    },
    null);

    var httpResponse = HttpContext.Response;
    httpResponse.Cookies.Remove("httpResponseCookie1");
}


Answer 6:

已经提供了答案了,但在owin 3.1.0,有可以使用的SystemWebChunkingCookieManager类。

https://github.com/aspnet/AspNetKatana/blob/dev/src/Microsoft.Owin.Host.SystemWeb/SystemWebChunkingCookieManager.cs

https://raw.githubusercontent.com/aspnet/AspNetKatana/c33569969e79afd9fb4ec2d6bdff877e376821b2/src/Microsoft.Owin.Host.SystemWeb/SystemWebChunkingCookieManager.cs

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    ...
    CookieManager = new SystemWebChunkingCookieManager()
    ...
});


Answer 7:

最快的一个行代码溶液:

HttpContext.Current.Session["RunSession"] = "1";

只需添加CreateIdentity方法之前,这条线:

HttpContext.Current.Session["RunSession"] = "1";
var userIdentity = userManager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie);
_authenticationManager.SignIn(new AuthenticationProperties { IsPersistent = rememberLogin }, userIdentity);


Answer 8:

我有设置Cookie的相同症状头球送交但这些答案的帮助了我。 一切工作我的本地机器上,但在部署到生产中的Set-Cookie头永远不会被设置。

原来,这是使用自定义的组合CookieAuthenticationMiddleware用的WebAPI沿的WebAPI压缩支持

幸运的是,我在我的项目,这让我记录此例外使用ELMAH:

后HTTP标头已被送往System.Web.HttpException服务器不能追加头。

这使我这个GitHub的问题

基本上,如果你有一个奇怪的设置像我一样,你会想禁用压缩您的WebAPI控制器/是设置cookies,或尝试的方法OwinServerCompressionHandler



文章来源: ASP.NET_SessionId + OWIN Cookies do not send to browser