首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >ASP.NET_SessionId + OWIN Cookie不会发送到浏览器

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

Stack Overflow用户
提问于 2013-12-23 13:39:35
回答 9查看 86.2K关注 0票数 161

我在使用Owin cookie身份验证时遇到了一个奇怪的问题。

当我启动时,我的IIS服务器身份验证在IE/Firefox和Chrome上工作得很好。

我开始在不同的平台上做一些认证和登录的测试,我发现了一个奇怪的错误。Owin framework / IIS偶尔不会向浏览器发送任何cookie。我将输入正确的用户名和密码,代码会运行,但根本没有cookie传递到浏览器。如果我重新启动服务器,它将开始工作,然后在某个时候,我将尝试登录,并再次停止发送cookie。单步执行代码不会做任何事情,也不会抛出错误。

代码语言:javascript
复制
 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);
                     }
                 }
               }
        });

,在我的登录过程中,我有以下代码:

代码语言:javascript
复制
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)打开一个新的浏览器(火狐)并尝试登录,它既不会收到ASP.NET_SessionId,也不会得到认证Cookie 4)当第一个浏览器有ASP.NET_SessionId时,它会继续工作。当我删除这个cookie时,它与我正在使用的所有其他浏览器的ip地址(10.x.x.x)和localhost都有相同的问题。

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

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

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

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

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

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

更新3B

经过一番挖掘后,我在一个页面上看到一些注释,上面写着“表单”身份验证超时和会话超时需要匹配。我认为这两者通常是同步的,但无论出于什么原因,这两者都不同步。

解决方法摘要

1)请始终在鉴权前先创建会话。基本上就是在启动应用程序Session["Workaround"] = 0;时创建会话

2)实验如果你持久化sessionTimeout,确保你的OWIN超时/长度比你web.config中的cookie长(在测试中)

EN

回答 9

Stack Overflow用户

回答已采纳

发布于 2014-01-20 20:49:47

我也遇到过同样的问题,并在ASP.NET托管实现中找到了原因。我会说这是个bug。

一些背景

我的发现基于这些程序集版本:

  • Microsoft.Owin,Version=2.0.2.0,主板,PublicKeyToken=31bf3856ad364e35
  • System.Web,Version=2.0.2.0,主板,PublicKeyToken=31bf3856ad364e35
  • Microsoft.Owin.Host.SystemWeb,Version=2.0.2.0,主板,Culture=neutral,主板,PublicKeyToken=31bf3856ad364e35
  • System.Web,Culture=neutral,主板,PublicKeyToken=31bf3856ad364e35
  • System.Web,Version=4.0.0.0,主板,Culture=neutral,主板

OWIN使用自己的抽象来处理响应Cookie (Microsoft.Owin.ResponseCookieCollection)。此实现直接包装响应标头集合,并相应地更新Set-Cookie标头。OWIN ASP.NET主机(Microsoft.Owin.Host.SystemWeb)只包装了System.Web.HttpResponse和它的headers集合。因此,当通过OWIN创建新cookie时,响应Set-Cookie头部将直接更改。

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

然后,在请求生存期的后期,测试Cookie更改的状态,并将(System.Web.HttpResponse.GenerateResponseHeadersForCookies())和HttpCookieCollection序列化到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属性为false。会话状态模块未生成任何会话id,并且System.Web.HttpResponse.Cookies集合状态为not detected as changed。在这种情况下,OWIN cookie被正确地发送到浏览器,并且登录工作。

情况2-在应用程序中的某处使用了会话,但在用户尝试对进行身份验证之前

System.Web.SessionState.SessionStateModule,s_sessionEverSet属性为true。会话Id是由SessionStateModule生成的,ASP.NET_SessionId被添加到System.Web.HttpResponse.Cookies集合中,但在稍后的请求生存期中会被删除,因为用户的会话实际上是空的。在这种情况下,System.Web.HttpResponse.Cookies收集状态为,检测为已更改的,并且在将Cookie序列化为标头值之前,首先清除Set-Cookie标头。

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

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

System.Web.SessionState.SessionStateModule,s_sessionEverSet属性为true。会话Id由SessionStateModule生成,ASP.NET_SessionId被添加到System.Web.HttpResponse.Cookies。由于System.Web.HttpCookieCollection和System.Web.HttpResponse.GenerateResponseHeadersForCookies()中的内部优化,Set-Cookie标头不会首先清除,而只是更新。

在这种情况下,OWIN身份验证cookie和ASP.NET_SessionId cookie都作为响应发送,登录工作正常。

cookies的更常见问题:

正如您所看到的,问题更为普遍,而且并不局限于ASP.NET会话。如果您通过Microsoft.Owin.Host.SystemWeb托管OWIN,并且您/某物直接使用System.Web.HttpResponse.Cookies集合,那么您将面临风险。

例如,this works和两个cookies都被正确地发送到浏览器...

代码语言:javascript
复制
public ActionResult Index()
{
    HttpContext.GetOwinContext()
        .Response.Cookies.Append("OwinCookie", "SomeValue");
    HttpContext.Response.Cookies["ASPCookie"].Value = "SomeValue";

    return View();
}

但这不是,OwinCookie是“”...

代码语言:javascript
复制
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项目模板测试的。

票数 166
EN

Stack Overflow用户

发布于 2016-02-23 21:16:14

简而言之,.NET cookie管理器将赢得OWIN cookie管理器的支持,并覆盖在OWIN层上设置的cookie。修复方法是使用SystemWebCookieManager class, provided as a solution on the Katana Project here。您需要使用此类或类似的类,该类将强制OWIN使用 cookie管理器,这样就不会出现不一致

代码语言:javascript
复制
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依赖项时指定它:

代码语言:javascript
复制
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    ...
    CookieManager = new SystemWebCookieManager()
    ...
});

这里提供了一个类似的答案,但它没有包括解决问题所需的所有代码库,所以我认为有必要在这里添加它,因为到Katana项目的外部链接可能会关闭,这也应该作为一个解决方案完整地记录在这里。

票数 53
EN

Stack Overflow用户

发布于 2014-11-18 01:17:28

从@TomasDolezal的伟大分析开始,我同时查看了Owin和System.Web源代码。

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

我已经做了一个小的中间件(sourcenuget)来做这件事,它的目的是放在cookie中间件注册的正上方。

代码语言:javascript
复制
app.UseKentorOwinCookieSaver();

app.UseCookieAuthentication(new CookieAuthenticationOptions());
票数 48
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20737578

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档