Пользователи ASP.NET 5 Identity 3 выходят из системы через некоторое время

Я использую биты RC1 и внешнюю (Google) аутентификацию, без Identity.EntityFramework.

Во время входа я устанавливаю флаг «Запомнить меня».

Вошедший в систему пользователь выдерживает перезапуск браузера (я вижу, что срок действия файла cookie истекает через 14 дней) и перезапуск веб-сайта.

Но после некоторого времени бездействия (около 15 минут), независимо от того, был перезапущен браузер/сайт или нет, обновление страницы приводит к выходу из системы, в журналах говорится:

info: Microsoft.AspNet.Authentication.Cookies.CookieAuthenticationMiddleware:
    AuthenticationScheme: Microsoft.AspNet.Identity.Application signed out.
    AuthenticationScheme: Microsoft.AspNet.Identity.External signed out.
    AuthenticationScheme: Microsoft.AspNet.Identity.TwoFactorUserId signed out.

Это похоже на "сеансы" в предыдущем ASP, но здесь я не использую никаких сеансов.

Это моя локальная машина разработчика, без IIS, прямое подключение Kestrel к порту 5000, так что это не проблема с защитой данных

Почему пользователь вынужден выйти?

Обновление: мой Startup.cs файл:

public void ConfigureServices(IServiceCollection services) 
{
    ....
    var identityBuilder = services
        .AddIdentity<User, UserRole>(options =>
        {
            options.User.AllowedUserNameCharacters = null;
            options.Cookies.ApplicationCookie.LoginPath = "/user/login";
            options.Cookies.ApplicationCookie.LogoutPath = "/user/logout";
        });
    identityBuilder.Services
        .AddScoped<IUserStore<User>, SportCmsDb>(serviceProvider => serviceProvider.GetService<SportCmsDb>())
        .AddScoped<IRoleStore<UserRole>, SportCmsDb>(serviceProvider => serviceProvider.GetService<SportCmsDb>());
    identityBuilder
        .AddDefaultTokenProviders();
    ....

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) 
{
    ....
    app.UseIdentity();
    app.UseGoogleAuthentication(options =>
    {
        options.ClientId = Configuration["OAuth:Google:ClientId"];
        options.ClientSecret = Configuration["OAuth:Google:Secret"];
    });
    ....

SportCmsDb это DbContext, а также реализует IUserStore<User>, IUserLoginStore<User>, IUserEmailStore<User>, IRoleStore<UserRole>, IUserClaimStore<User>

Обновление 2

Я включил подробное (отладочное) ведение журнала и обнаружил, что когда пользователь выходит из системы, до этого вызывается мой IUserStore<User>.FindByIdAsync. С реальным/существующим идентификатором пользователя и функцией, возвращающей правильный ненулевой пользователь. Все кажется хорошим. Но моя загруженная из базы данных User "отклонена" и вынуждена выйти из системы. Нет дополнительных сообщений журнала, которые могут показать, почему/где.


person Dmitry    schedule 11.05.2016    source источник
comment
Не могли бы вы включить свой вызов services.AddIdentity в ConfigureServices или вы полностью его удалили? Вы все еще используете app.UseIdentity(); в методе Startup.Configure?   -  person Ron DeFreitas    schedule 11.05.2016
comment
Обновлен мой пост с частями Startup.cs   -  person Dmitry    schedule 12.05.2016


Ответы (3)


Вау, я решил это!

TL;DR

Мне нужно реализовать IUserSecurityStampStore<User> на моем пользовательском UserManager (также известном как SportCmsDb).

Подробнее

Во время вызова AddIdentity (в методе Startup.cs ConfigureServices) IdentityOptions настроены с помощью экземпляр IdentityCookieOptions по умолчанию. В конструкторе IdentityCookieOptions создается экземпляр ApplicationCookie (типа CookieAuthenticationOptions) с обработчиком CookieAuthenticationEvents.OnValidatePrincipal установлен в SecurityStampValidator.ValidatePrincipalAsync статический метод.

Во время вызова UseIdentity (в методе Startup.cs Configure) CookieAuthenticationMiddleware равно настроен с IdentityOptions.Cookies.ApplicationCookie параметрами.

CookieAuthenticationHandler (созданный CookieAuthenticationMiddleware) в своем методе HandleAuthenticateAsync считывает билет из файла cookie и вызывает Options.Events.ValidatePrincipal обработчик проверки.

Фактически вызывается SecurityStampValidator.ValidatePrincipalAsync. Этот метод проверяет, что прошло достаточно времени с момента создания файла cookie (30 минут по умолчанию) и вызывает ISecurityStampValidator.validateAsync (строки 81–82).

Реализация ISecurityStampValidator по умолчанию — SecurityStampValidator<TUser>. Он вызывает SignInManager<TUser>.ValidateSecurityStampAsync и когда возвращается null - отклоняет принципала и заставляет пользователя выйти из системы (строки 30-40).

SignInManager<TUser> в его Метод ValidateSecurityStampAsync пытается прочитать метку безопасности из User и возвращает null, если не может (если UserManager<User> не поддерживает этот интерфейс) или метка не соответствует сохраненной (в файле cookie).

Мой пользовательский UserManager не реализует IUserSecurityStampStore<User>. Бинго.

person Dmitry    schedule 12.05.2016
comment
Привет, я реализовал интерфейс IUserSecurityStampStore в своем пользовательском хранилище UserStore и просто сохранил штамп в пользователе (SetSecurityStampAsync) и вернул его (GetSecurityStampAsync), но метод Set никогда не вызывался? В методе ValidateSecurityStampAsync из кода asp.net я увидел, что они получают заявку с именем SecurityStampClaimType? Кто несет ответственность за установление этого требования? Мне ? В моем случае у меня нет пользовательского UserManager или SignInManager, только магазин является пользовательским, и я получаю только пользователя, а не создаю пользователей вообще. - person Dilyan Dimitrov; 17.05.2016
comment
Вы должны вручную вызывать SetSecurityStampAsync только тогда, когда вам нужно повторно войти в систему (например, после изменения его ролей/прав на странице администратора). Утверждение SecurityStampClaimType установлено внутри (с утверждениями идентификатора пользователя и имени), не беспокойтесь об этом. Но у вас должно быть некоторое начальное значение штампа безопасности - GetSecurityStampAsync не должно возвращать значение null. - person Dmitry; 17.05.2016
comment
Хорошо, я думал, что все должно быть правильно, но безуспешно :( Я указал значение штампа в претензиях, но GetSecurityStampAsync (возврат того же значения, которое я указал в претензии) так и не позвонил.. 30 минут ожидания теста суровы: ) - person Dilyan Dimitrov; 17.05.2016
comment
Установите options.SecurityStampValidationInterval = TimeSpan.FromMinutes(1) в Startup.cs при настройке IdentityOptions - person Dmitry; 17.05.2016
comment
Хорошо.. теперь все работает! Теперь я понимаю всю эту идею. Большое спасибо за помощь, вы спасли мой день :) - person Dilyan Dimitrov; 17.05.2016
comment
Спасибо. Эта проблема сводила меня с ума. - person Oliver Weichhold; 01.11.2016

Большое спасибо за предыдущие ответы, сегодня я работал с этой проблемой и решил это:

1.- Пользовательский UserStore.cs:

public class UserStore : IUserStore<User>,                                                                                              
    IUserPasswordStore<User>,                                                                                                           
    IUserEmailStore<User>,                                                                                                              
    IUserRoleStore<User>,                                                                                                               
    IUserSecurityStampStore<User>                                                                                                       
{  
    //omitted...

    public Task SetSecurityStampAsync(User user, string stamp, CancellationToken cancellationToken = default(CancellationToken))
    {
        user.SecurityStamp = stamp;
        return Task.FromResult(0);
    }

    public Task<string> GetSecurityStampAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
    {
        if (user.SecurityStamp == null) {
            return Task.FromResult("AspNet.Identity.SecurityStamp");
        }
        return Task.FromResult(user.SecurityStamp);
    }
}

2.- В User.cs и DB Table User добавьте SecurityStamp в виде строки.

Для ТЕСТА измените значение по умолчанию с 30 м на 1 м в Startup.cs:

services.Configure<SecurityStampValidatorOptions>(options => {        
    options.ValidationInterval = TimeSpan.FromMinutes(1);                  
});
person Uriel    schedule 22.11.2018
comment
Это решило мою проблему с выходом из системы в ASPNETCORE 2.1. Моя проблема заключалась в использовании пользовательского хранилища, которое не реализовывало «IUserSecurityStampStore» (кто знал?!), и думало, что ошибка связана с куки-файлами со скользящим сроком действия. Единственная разница в моем коде с кодом Уриэля заключается в том, что «пользователь-пользователь» был «пользователем CustomUser». - person Sid James; 23.01.2019

В моем случае я использовал сервер идентификации и столкнулся с той же проблемой. Проблема заключалась в том, что я не добавлял утверждение SecurityStamp в файл cookie в первую очередь, поэтому при вызове SecurityStampValidator он всегда возвращал false.

Код перед:

var user = await this.loginService.GetOrCreateUser(claimsPrincipal, provider);    
    
var localSignInProps = this.GetAuthenticationProperties(result);

// issue authentication cookie for user
var identityServerUser = new IdentityServerUser(user.Id)
{
    DisplayName = user.UserName,
    IdentityProvider = provider
};

await this.HttpContext.SignInAsync(identityServerUser, localSignInProps);

Код после:

var user = await this.loginService.GetOrCreateUser(claimsPrincipal, provider);    
var principal = await this.signInManager.CreateUserPrincipalAsync(user);
    
var localSignInProps = this.GetAuthenticationProperties(result);

// issue authentication cookie for user
var identityServerUser = new IdentityServerUser(user.Id)
{
    DisplayName = user.UserName,
    IdentityProvider = provider,
    AdditionalClaims = principal.Claims.ToList(),
};

await this.HttpContext.SignInAsync(identityServerUser, localSignInProps);

Принципал содержит требование, которое выглядит следующим образом:

{AspNet.Identity.SecurityStamp: BBDVFD2WCAZTMA75O3IQKPUWNKKOZOL5}
person Mariano Soto    schedule 04.06.2021