with net mvc existing asp adddefaultidentity c# asp.net-core asp.net-core-mvc asp.net-identity-2

net - authorize c# mvc



Identidad ASP.NET 5-SignInManager personalizado (4)

El problema aquí fue que no proporcioné AuthenticationType de mi objeto ClaimsIdentity . Esta publicación en el blog me ayudó.

Para que IsAuthenticated se establezca en verdadero, debe especificar un tipo de autenticación en el ctor:

var id = new ClaimsIdentity (claims, "Custom");

Tengo un proyecto de MVC 6 (vNext) y estoy jugando con la identidad de ASP.NET. En mi caso, no quiero usar el material incorporado que usa EF (SignInManager, UserManager, UserStore). Tengo una base de datos externa y solo quiero hacer una búsqueda de nombre de usuario / contraseña y devolver una cookie válida. Entonces comencé a escribir mis propias clases.

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> { ... }

En la clase MyUserStore , estoy usando una lista de usuarios codificada como mi tienda (solo para fines de prueba). Y eliminé algunos métodos solo para devolver los datos de la tienda codificada.

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) { } }

Aquí hice los métodos CheckPasswordAsync y VerifyPasswordAsync para devolver true y PasswordVerificationResult.Success respectivamente, solo para la prueba.

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

Y todo está conectado en la clase de Startup la siguiente manera:

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

Y debido a que no pude crear el objeto MySignInManager en el código de Startup para agregarlo a la DI (para una posterior inyección en los controladores y las vistas), lo estoy creando en MyAccountController .

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

En mi acción MyLogin en el controlador MyAccount llamo PasswordSignInAsync y puedo ver que MyAccount la cookie con los reclamos codificados (desde MyClaimsPrincipleFactory ). Cuando intento llamar a otra acción con AuthorizeAttribute , veo que la cookie está en el encabezado de la solicitud, pero no estoy autorizado (más precisamente, porque no eliminé la autenticación de identidad de ASP.NET incorporada predeterminada del plantilla de muestra de Visual Studio, en su lugar, se me redirige a la cuenta / inicio de sesión).

¿Es esta la manera correcta de personalizar la identidad de ASP.NET y qué es lo que me falta aquí?


En mi proyecto, tengo una implementación de identidad funcional sin usar EF. Creo que quizás estás implementando más de lo necesario. UserManager y SignInManager no están vinculados a EF. Puede implementarlos si lo desea, pero no tiene que simplemente alejarse de EF. solo necesita implementar UserStore y RoleStore y quizás PasswordHasher si necesita validar la contraseña codificada para la prueba.

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

lo anterior muestra los elementos que estoy implementando para eludir Entity Framework, mi AccountController por ejemplo toma los parámetros del constructor para

UserManager<SiteUser> userManager, SignInManager<SiteUser> signInManager

que son la identidad estándar de UserManager y SignInManager que no tuve que configurar con los servicios de DI, están registrados para mí en esta línea:

services.AddIdentity<SiteUser, SiteRole>();

puedes ver el código para ese método de extensión aquí . Es parte de Identity que no forma parte de EFIdentity. Puede ver que también implementé IUserClaimsPrincipalFactory. La única razón por la que implementé eso fue para agregar algunas reclamaciones personalizadas, no tuve que hacer eso para alejarme de EF.


Puede registrar un SignInManager personalizado para Dependency Injection utilizando el método IdentityBuilder.AddSignInManager dentro de su método Startup.ConfigureServices siguiente manera:

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

No hay ninguna razón para que el SignInManager que ha implementado no pueda registrarse para DI de la misma manera.


También tuve problemas al tratar de usar un SignInManager personalizado y resultó ser muy fácil de implementar.

En Startup.cs , después de la implementación predeterminada de services.Identity

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

Solo necesita inyectar en la DI incorporada lo siguiente:

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

El administrador de SignInManager predeterminado es sobreescrito por el personalizado.