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

ASP.NET 5 Identity - пользовательский SignInManager

У меня есть проект MVC 6 (vNext), и я играю с идентификатором ASP.NET. В моем случае я не хочу использовать встроенный материал, который использует EF (SignInManager, UserManager, UserStore). У меня есть внешняя база данных, и я просто хочу найти имя пользователя/пароль и вернуть действительный файл cookie. Поэтому я начал писать свои собственные классы.

public class MyUser
{
    public string Id { get; set; }
    public string UserName { get; set; }
    public string Password { get; set; }
    public string PasswordHash { get; set; }
}

public class MyUserStore : IUserStore<MyUser>, IUserPasswordStore<MyUser>
{
    ...
}

В классе MyUserStore я использую жестко запрограммированный список пользователей в качестве своего магазина (только для тестовых целей). И я перепробовал некоторые методы только для того, чтобы вернуть данные из жестко закодированного хранилища.

public class MyUserManager : UserManager<MyUser>
{
    public MyUserManager(
        IUserStore<MyUser> store,
        IOptions<IdentityOptions> optionsAccessor,
        IPasswordHasher<MyUser> passwordHasher,
        IEnumerable<IUserValidator<MyUser>> userValidators,
        IEnumerable<IPasswordValidator<MyUser>> passwordValidators,
        ILookupNormalizer keyNormalizer,
        IdentityErrorDescriber errors,
        IEnumerable<IUserTokenProvider<MyUser>> tokenProviders,
        ILoggerFactory logger,
        IHttpContextAccessor contextAccessor) :
        base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, tokenProviders, logger, contextAccessor)
    {
    }
}

Здесь я сделал методы CheckPasswordAsync и VerifyPasswordAsync для возврата true и PasswordVerificationResult.Success соответственно только для теста.

public class MyClaimsPrincipleFactory : IUserClaimsPrincipalFactory<MyUser>
{
    public Task<ClaimsPrincipal> CreateAsync(MyUser user)
    {
        return Task.Factory.StartNew(() =>
        {
            var identity = new ClaimsIdentity();
            identity.AddClaim(new Claim(ClaimTypes.Name, user.UserName));
            var principle = new ClaimsPrincipal(identity);

            return principle;
        });
    }
}

public class MySignInManager : SignInManager<MyUser>
{
    public MySignInManager(MyUserManager userManager, IHttpContextAccessor contextAccessor, IUserClaimsPrincipalFactory<MyUser> claimsFactory, IOptions<IdentityOptions> optionsAccessor = null, ILoggerFactory logger = null)
            : base(userManager, contextAccessor, claimsFactory, optionsAccessor, logger)
    {
    }

    public override Task<SignInResult> PasswordSignInAsync(string userName, string password, bool isPersistent, bool shouldLockout)
    {
        // here goes the external username and password look up

        if (userName.ToLower() == "username" && password.ToLower() == "password")
        {
            return base.PasswordSignInAsync(userName, password, isPersistent, shouldLockout);
        }
        else
        {
            return Task.FromResult(SignInResult.Failed);
        }
    }
}

И все подключено в классе Startup следующим образом:

services.AddIdentity<MyUser, MyRole>()
            .AddUserStore<MyUserStore>()
            .AddUserManager<MyUserManager>()
            .AddDefaultTokenProviders();

И поскольку мне не удалось создать объект MySignInManager в коде Startup, чтобы добавить его в DI (для последующей инъекции в контроллерах и представлениях), я создаю его в MyAccountController.

public MyAccountController(IHttpContextAccessor httpContextAccessor, UserManager<MyUser> userManager, IOptions<IdentityOptions> optionsAccessor, ILoggerFactory logger)
{
    SignInManager = new MySignInManager(userManager as MyUserManager, httpContextAccessor, new MyClaimsPrincipleFactory(), optionsAccessor, logger);
}

В моем действии MyLogin в контроллере MyAccount я звоню PasswordSignInAsync, и я вижу, что я получаю файл cookie с закодированными утверждениями в нем (из MyClaimsPrincipleFactory). Когда я пытаюсь вызвать какое-то другое действие с помощью AuthorizeAttribute на нем, я вижу, что cookie находится в заголовке запроса, но я несанкционирован (точнее, потому что я не удалял встроенную стандартную аутентификацию ASP.NET Identity Authentication из шаблона образца визуальной студии я перенаправлен на учетную запись /Login ).

Является ли это правильным способом настройки ASP.NET Identity и чего я здесь не вижу?

4b9b3361

Ответ 1

В моем проекте у меня есть работающая реализация идентичности без использования EF. Я думаю, может быть, вы внедряете больше, чем вам нужно. UserManager и SignInManager не привязаны к EF. Вы можете реализовать их, если хотите, но вам не нужно просто уйти от EF. вам действительно нужно реализовать UserStore и RoleStore и, возможно, PasswordHasher, если вам нужно проверить пароль с жестким кодом для тестирования.

services.TryAdd(ServiceDescriptor.Scoped<IUserStore<SiteUser>, UserStore<SiteUser>>());
services.TryAdd(ServiceDescriptor.Scoped<IUserPasswordStore<SiteUser>, UserStore<SiteUser>>());
services.TryAdd(ServiceDescriptor.Scoped<IUserEmailStore<SiteUser>, UserStore<SiteUser>>());
services.TryAdd(ServiceDescriptor.Scoped<IUserLoginStore<SiteUser>, UserStore<SiteUser>>());
services.TryAdd(ServiceDescriptor.Scoped<IUserRoleStore<SiteUser>, UserStore<SiteUser>>());
services.TryAdd(ServiceDescriptor.Scoped<IUserClaimStore<SiteUser>, UserStore<SiteUser>>());
services.TryAdd(ServiceDescriptor.Scoped<IUserPhoneNumberStore<SiteUser>, UserStore<SiteUser>>());
services.TryAdd(ServiceDescriptor.Scoped<IUserLockoutStore<SiteUser>, UserStore<SiteUser>>());
services.TryAdd(ServiceDescriptor.Scoped<IUserTwoFactorStore<SiteUser>, UserStore<SiteUser>>());
services.TryAdd(ServiceDescriptor.Scoped<IRoleStore<SiteRole>, RoleStore<SiteRole>>());
services.TryAdd(ServiceDescriptor.Scoped<IUserClaimsPrincipalFactory<SiteUser>, SiteUserClaimsPrincipalFactory<SiteUser, SiteRole>>());
services.TryAdd(ServiceDescriptor.Transient<IPasswordHasher<SiteUser>, SitePasswordHasher<SiteUser>>());
services.AddIdentity<SiteUser, SiteRole>();

выше показаны элементы, которые я реализую, чтобы обойти Entity Framework, например, мой AccountController принимает параметры конструктора для

    UserManager<SiteUser> userManager,
            SignInManager<SiteUser> signInManager

которые являются стандартными идентификаторами UserManager и SignInManager, которые мне не нужно настраивать с помощью служб DI, которые они регистрируют для меня по этой строке:

services.AddIdentity<SiteUser, SiteRole>();

вы можете посмотреть здесь код этого метода расширения. Это часть Identity, не являющаяся частью EFIdentity. Вы можете видеть, что я также реализовал IUserClaimsPrincipalFactory. Единственная причина, по которой я реализовал это: добавить некоторые пользовательские требования, мне не нужно было это делать, чтобы уйти от EF.

Ответ 2

У меня возникли проблемы с попыткой использовать пользовательский SignInManager и, как оказалось, очень просто реализовать.

В Startup.cs, после реализации по умолчанию services.Identity

services.AddIdentity<ApplicationUser, IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders();

Вам нужно только ввести во встроенный DI следующее:

services.AddScoped<SignInManager<MyApplicationUser>, MySignInManager>();

По умолчанию SignInManager перезаписывается пользовательским.

Ответ 3

Проблема заключалась в том, что я не предоставлял AuthenticationType моего объекта ClaimsIdentity. Это сообщение в блоге помогло мне.

Чтобы установить IsAuthenticated в значение true, вам нужно указать тип аутентификации в ctor:

var id = new ClaimsIdentity (претензии, "Пользовательский" );

Ответ 4

Вы можете зарегистрировать пользовательский SignInManager для Injection Dependency, используя метод IdentityBuilder.AddSignInManager в вашем методе Startup.ConfigureServices следующим образом:

services.AddIdentity<MyUser, IdentityRole<int>>()
    .AddUserStore<UserStore<MyUser, IdentityRole<int>, SqlContext, int>>()
    .AddRoleStore<RoleStore<IdentityRole<int>, SqlContext, int>>()
    .AddSignInManager<SignInManager<MyUser>>()
    .AddDefaultTokenProviders();

Нет причин, по которым реализованный SignInManager не может быть зарегистрирован для DI таким же образом.