Подтвердить что ты не робот

ExpireTimeSpan игнорируется после регенерацииIdentity/validateInterval длительность в MVC Identity (2.0.1)

Я весь день царапал себе голову над этим. Я пытаюсь настроить "очень длинные" сеансы входа в MVC Identity 2.0.1. (30 дней).

Я использую следующий запуск файла cookie:

      app.UseCookieAuthentication(new CookieAuthenticationOptions
        {

            SlidingExpiration = true,
            ExpireTimeSpan = System.TimeSpan.FromDays(30),
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            LoginPath = new PathString("/My/Login"),
            CookieName = "MyLoginCookie",
            Provider = new CookieAuthenticationProvider
            {                           
                OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
                    validateInterval: TimeSpan.FromMinutes(30),

                    regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
            }
        });

Что в целом работает отлично. Печенье установлено через 30 дней, все выглядит хорошо.

Если я закрою браузер и вернусь после того, как прошло "validateInterval" (30 минут здесь), я все еще зарегистрирован, однако cookie теперь переиздается только как "сессия" (правильное имя файла cookie)! 30-дневное истечение закончилось.

Если я сейчас закрою браузер/снова заново, я больше не вошел в систему.

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

4b9b3361

Ответ 1

Когда SecurityStampValidator запускает обратный вызов regenerateIdentity, текущий аутентифицированный пользователь получает повторную подписку с непостоянным входом в систему. Это жестко закодировано, и я не верю, что есть какой-либо способ напрямую его контролировать. Таким образом, сеанс входа в систему будет продолжаться только до конца сеанса браузера, который вы выполняете в точке, в которой регенерируется идентификатор.

Ниже приведен подход, позволяющий сделать логин постоянным, даже при выполнении операций регенерации идентичности. Это описание основано на использовании шаблонов веб-проектов Visual Studio MVC ASP.NET.

Сначала нам нужно иметь возможность отслеживать тот факт, что сеанс регистрации является постоянным в разных HTTP-запросах. Это можно сделать, добавив претензию "IsPersistent" к идентификатору пользователя. Следующие способы расширения показывают способ сделать это.

public static class ClaimsIdentityExtensions
{
    private const string PersistentLoginClaimType = "PersistentLogin";

    public static bool GetIsPersistent(this System.Security.Claims.ClaimsIdentity identity)
    {
        return identity.Claims.FirstOrDefault(c => c.Type == PersistentLoginClaimType) != null;
    }

    public static void SetIsPersistent(this System.Security.Claims.ClaimsIdentity identity, bool isPersistent)
    {
        var claim = identity.Claims.FirstOrDefault(c => c.Type == PersistentLoginClaimType);
        if (isPersistent)
        {
            if (claim == null)
            {
                identity.AddClaim(new System.Security.Claims.Claim(PersistentLoginClaimType, Boolean.TrueString));
            }
        }
        else if (claim != null)
        {
            identity.RemoveClaim(claim);
        }
    }
}

Далее нам нужно сделать запрос "IsPersistent", когда пользователь подписывается с запросом на постоянный сеанс. Например, ваш класс ApplicationUser может иметь метод GenerateUserIdentityAsync, который может быть обновлен, чтобы принять параметр флага isPersistent следующим образом, чтобы сделать такое требование при необходимости:

public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager, bool isPersistent)
{
    var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
    userIdentity.SetIsPersistent(isPersistent);
    return userIdentity;
}

Любые вызывающие абоненты ApplicationUser.GenerateUserIdentityAsync теперь должны будут передать флаг isPersistent. Например, вызов GenerateUserIdentityAsync в AccountController.SignInAsync изменился бы с

AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, 
    await user.GenerateUserIdentityAsync(UserManager));

к

AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent },
    await user.GenerateUserIdentityAsync(UserManager, isPersistent));

Наконец, делегат CookieAuthenticationProvider.OnValidateIdentity, используемый в методе Startup.ConfigureAuth, нуждается в некотором внимании, чтобы сохранить детали персистентности при выполнении операций регенерации идентичности. Делегат по умолчанию выглядит так:

OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
    validateInterval: TimeSpan.FromMinutes(20),
    regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))

Это можно изменить на:

OnValidateIdentity = async (context) =>
{
    await SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
        validateInterval: TimeSpan.FromMinutes(20),
        // Note that if identity is regenerated in the same HTTP request as a logoff attempt,
        // the logoff attempt will have no effect and the user will remain logged in.
        // See https://aspnetidentity.codeplex.com/workitem/1962
        regenerateIdentity: (manager, user) =>
            user.GenerateUserIdentityAsync(manager, context.Identity.GetIsPersistent())
    )(context);

    // If identity was regenerated by the stamp validator,
    // AuthenticationResponseGrant.Properties.IsPersistent will default to false, leading
    // to a non-persistent login session. If the validated identity made a claim of being
    // persistent, set the IsPersistent flag to true so the application cookie won't expire
    // at the end of the browser session.
    var newResponseGrant = context.OwinContext.Authentication.AuthenticationResponseGrant;
    if (newResponseGrant != null)
    {
        newResponseGrant.Properties.IsPersistent = context.Identity.GetIsPersistent();
    }
}