ASP.NET файлы cookie SessionId + OWIN не отправляются в браузер

у меня странная проблема с использованием аутентификации OWIN cookie.

когда я запускаю аутентификацию сервера IIS, она отлично работает в IE / Firefox и Chrome.

Я начал делать некоторые тесты с аутентификацией и входом в систему на разных платформах, и я придумал странную ошибку. Спорадически OWIN framework / 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) (Chrome) при входе в систему я получаю ASP.NET_SessionId + мой файл cookie аутентификации. 2) захожу на страницу, которая устанавливает сеанс.содержание... 3) откройте новый браузер (Firefox) и попробуйте войти в систему, и он не получит ASP.NET_SessionId не получает файл Cookie проверки подлинности 4) в то время как первый браузер имеет ASP.NET_SessionId продолжает работать. В ту минуту, когда я удаляю этот файл cookie, у него такая же проблема, как и у всех других броузеры Я работаю над ip-адресом (10.X. X. x) и localhost.

обновление 2: сила создания ASPNET_SessionId сначала на моей странице login_load перед аутентификацией с OWIN.

1) Прежде чем я проверить подлинность долг я сделать случайную Session.Content значение на моей странице входа в систему, чтобы запустить ASP.NET_SessionId 2) Затем я проверить подлинность и делать дальнейшие сеансы 3) другие браузеры, похоже, теперь работают

это странно. Я могу только заключить, что это имеет какое-то отношение с мышлением ASP и OWIN они находятся в разных областях или что-то в этом роде.

обновление 3 - странное поведение между двумя.

обнаружено дополнительное странное поведение-тайм-аут сеанса Owin и ASP отличается. Я вижу, что мои сеансы Owin остаются живыми дольше, чем мои сеансы ASP через какой-то механизм. Поэтому при входе в: 1.) У меня есть сеанс аутентификации на основе cookied 2.) Я установил несколько переменных сеанса

мой переменные сеанса (2) "умирают" до того, как переменная сеанса cookie owin заставляет повторно войти в систему, что вызывает неожиданное поведение во всем моем приложении. (Человек вошел в систему, но на самом деле не вошел в систему)

обновление 3B

после некоторого рытья я увидел некоторые комментарии на странице, которые говорят, что тайм-аут аутентификации "форм" и тайм-аут сеанса должны совпадать. Я думаю, что обычно они синхронизированы, но по какой-то причине они не в синхронизация.

резюме решений

1) Всегда сначала создайте сеанс перед аутентификацией. В основном создать сеанс при запуске приложения Session["Workaround"] = 0;

2) [экспериментальный] если вы сохраняете cookies, убедитесь, что ваш тайм-аут / длина OWIN длиннее, чем ваш sessionTimeout в вашем интернете.config (в тестирование)

8 ответов


я столкнулся с той же проблемой и проследил причину до OWIN ASP.NET реализация хостинга. Я бы сказал, что это ошибка.

некоторые предпосылки

мои выводы основаны на этих версиях сборки:

  • Microsoft.Owin, Version=2.0.2.0, Culture=нейтральный, PublicKeyToken=31bf3856ad364e35
  • Microsoft.Оуин.Хозяин.SystemWeb, версия=2.0.2.0, культура=нейтральная, PublicKeyToken=31bf3856ad364e35
  • это работает и оба куки правильно отправляются в браузер...
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 по умолчанию.


начиная с большого анализа @TomasDolezal, я посмотрел как на Owin, так и на систему.Веб-источник.

проблема в этой системе.Веб имеет свой собственный главный источник информации cookie, и это не заголовок Set-Cookie. Owin знает только о заголовке Set-Cookie. Обходной путь - убедиться, что любые куки, установленные Owin, также установлены в HttpContext.Current.Response.Cookies коллекция.

Я сделал небольшое промежуточное ПО (источник, NuGet для), что делает именно то, что предназначено для размещения непосредственно над регистрацией промежуточного программного обеспечения cookie.

app.UseKentorOwinCookieSaver();

app.UseCookieAuthentication(new CookieAuthenticationOptions());

короче, менеджер файлов cookie .NET выиграет над менеджером файлов cookie OWIN и перезапишет файлы cookie, установленные на уровне OWIN. Исправление заключается в использовании класс SystemWebCookieManager, предоставленный в качестве решения для проекта Katana здесь. Вам нужно использовать этот класс или аналогичный ему, который будет заставить OWIN использовать диспетчер файлов cookie .NET, чтобы не было несоответствий:

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()
    ...
});

аналогичный ответ был предоставлен здесь, но он не включает в себя всю кодовую базу, необходимую для решения проблемы, поэтому я вижу необходимость добавить его здесь, потому что внешняя ссылка на проект Katana может пойти вниз, и это должно быть полностью записано как решение здесь.


команда Катана ответила на вопрос Томас Долезар поднял и вывесил документация о обходных путях:

обходные пути делятся на две категории. Один переконфигурировать Система.Web, поэтому он избегает использования ответа.Сбор печенье и перезапись печенья OWIN. Другой подход заключается в повторной настройке затронутые компоненты OWIN, поэтому они пишут cookies непосредственно в Система.Ответ веба.Коллекция печенье.

  • убедитесь, что сеанс установлен до аутентификации: конфликт между системой.Куки-файлы Web и Katana по запросу, поэтому это может быть возможно, приложение установит сессию по какой-либо просьбе до прохождения проверки подлинности. Это должно быть легко сделать, когда пользователь сначала прибывает, но это может быть сложнее гарантировать позже, когда срок действия сеансовых или аутентификационных файлов cookie истекает и / или их необходимо обновить.
  • отключить SessionStateModule-если приложение не полагается на информацию о сеансе, но модуль сеанса по-прежнему устанавливает cookie, который вызывает вышеуказанный конфликт, затем вы можете отключить модуль состояния сеанса.
  • перенастроить CookieAuthenticationMiddleware для записи непосредственно в систему.Коллекция файлов cookie Web.
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;
}

(боковое Примечание: метод Initialize выше является логическим местом для исправления, потому что база.Initialize делает сеанс доступным. Однако исправление может также будет применено позже, потому что в OpenId сначала анонимный запрос, затем перенаправление к поставщику OpenId, а затем обратно в приложение. Проблемы возникнут после перенаправления обратно в приложение, в то время как исправление устанавливает переменную сеанса уже во время первого анонимного запроса, таким образом, исправляя проблему, прежде чем произойдет перенаправление назад)

Изменить 2

копировать-вставить проект Катана 2016-05-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),
            });
    }
}

ответы уже предоставлены, но в 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()
    ...
});

если вы сами устанавливаете cookies в промежуточном по OWIN, используйте 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");
}

самое быстрое однострочное решение кода:

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);

у меня был тот же симптом заголовка Set-Cookie, который не был отправлен, но ни один из этих ответов не помог мне. Все работало на моей локальной машине, но при развертывании для производства заголовки set-cookie никогда не будут установлены.

оказывается, это была комбинация использования пользовательского CookieAuthenticationMiddleware с WebApi вместе с поддержка сжатия WebApi

к счастью, я использовал ELMAH в своем проекте, который позволил мне регистрировать это исключение: