2015-07-07 2 views
21

Ho un progetto MVC 6 (vNext) e sto giocando con l'identità di ASP.NET. Nel mio caso non voglio usare la componente build-in che utilizza EF (SignInManager, UserManager, UserStore). Ho un database esterno e voglio solo fare una ricerca username/password e restituire un cookie valido. Così ho iniziato a scrivere le mie lezioni.ASP.NET 5 Identity - SignInManager personalizzato

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

Nella classe MyUserStore sto utilizzando lista hard-coded di utenti come il mio negozio (solo a scopo di test). E ho annullato alcuni metodi solo per restituire i dati dal negozio hard-coded.

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

Qui ho fatto i metodi CheckPasswordAsync e VerifyPasswordAsync per tornare true e PasswordVerificationResult.Success, rispettivamente, solo per il test.

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

e tutto è collegato nella classe Startup come segue:

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

E perché io non sono riuscito a creare l'oggetto MySignInManager nel codice Startup al fine di aggiungerlo nel DI (per l'iniezione successiva nei controller e nelle viste), lo sto creando nello MyAccountController.

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

Nel mio MyLogin azione nel controller MyAccount sto chiamando PasswordSignInAsync e posso vedere che sto ottenendo il cookie con rivendicazioni codificati in esso (dal MyClaimsPrincipleFactory). Quando provo a chiamare qualche altra azione con lo AuthorizeAttribute su di esso, posso vedere che il cookie è nell'intestazione della richiesta ma non sono autorizzato (più precisamente, perché non ho rimosso l'autenticazione di identità predefinita ASP.NET da il modello di esempio dello studio visivo, sono reindirizzato invece all'Account/Login).

È questo il modo giusto di personalizzare l'identità di ASP.NET e cosa mi manca qui?

+0

Inserire un punto di interruzione su questa riga return base.PasswordSignInAsync (userName, password, isPersistent, shouldLockout); e vedere cosa restituisce. La mia ipotesi è che anch'essa restituisca SignInResult.Failed. Vorrei tornare Task.FromResult (SignInResult.Success) invece di chiamare la classe base ... hth – ojf

+0

Sì, l'ho provato. Quando ritorno con successo senza chiamare il metodo base, non ricevo il cookie. –

risposta

5

Nel mio progetto ho un'implementazione funzionante dell'identità senza utilizzare EF. Penso che forse stai implementando più del necessario. UserManager e SignInManager non sono legati a EF. Puoi implementarli se vuoi, ma non devi solo fuggire da EF. è solo necessario implementare UserStore e RoleStore e forse PasswordHasher se è necessario convalidare la password codificata per il test.

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

mostra gli articoli che sono l'attuazione di bypassare Entity Framework, mia AccountController per esempio prende parametri del costruttore per

UserManager<SiteUser> userManager, 
      SignInManager<SiteUser> signInManager 

che sono l'UserManager identità standard ed SignInManager che io non ho dovuto installazione con dI servizi sono registrati per me da questa linea:

services.AddIdentity<SiteUser, SiteRole>(); 

è possibile see the code for that extension method here. Fa parte di Identity non parte di EFIdentity. Si può vedere che ho implementato anche IUserClaimsPrincipalFactory. L'unica ragione per cui l'ho implementato è stata l'aggiunta di alcune dichiarazioni personalizzate, non ho avuto bisogno di farlo per scappare da EF.

+0

Forse non ho davvero avuto bisogno di implementare IRoleStore dato che non è effettivamente utilizzato da nessuna parte nel codice dell'app web starter. La mia "implementazione" lancia semplicemente NotImplementedException in ogni metodo, ma non ho mai incontrato l'eccezione perché non è usata da nessuna parte. Presumibilmente nelle versioni future del modello di app Web VS ci saranno controller/viste per gestire i ruoli e sarà necessario allora ma non ora. –

+0

Okey, ho rimosso il mio personalizzato 'SignInManager' e' UserManager'. Ancora quando provo ad andare all'azione con l'attributo 'Autorizza' sto ottenendo 302 e mi reindirizza al/Account/Login - quello predefinito. Ma ho MyAccountController che funziona con il mio IUserStore personalizzato, MyUser, ecc .... –

+0

Mi sono reso conto che dopo aver effettuato l'accesso ho il mio 'ClaimsIdentity' nell'oggetto' User.Identity'. Ma la proprietà 'IsAuthenticated' è' false'. Nel violinista posso vedere che il cookie è lì ed è passato con ogni richiesta. –

3

Il problema qui era che non ho fornito il AuthenticationType del mio oggetto ClaimsIdentity. This blog post mi ha aiutato.

Aver IsAuthenticated impostata su true, è necessario specificare un tipo di autenticazione nel ctor:

var id = new ClaimsIdentity (reclami, “Custom”);

5

Ho avuto problemi anche a cercare di usare un SignInManager personalizzato e risulta essere davvero facile dopo tutto da implementare.

In Startup.cs, dopo l'implementazione predefinita di services.Identity

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

Hai solo bisogno di iniettare nel incorporato DI seguente:

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

L'impostazione predefinita SignInManager viene sovrascritto da quello personalizzato .

1

È possibile registrare un personalizzato SignInManager per Dependency Injection utilizzando il metodo IdentityBuilder.AddSignInManager all'interno del vostro metodo di Startup.ConfigureServices come segue:

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

Non v'è alcuna ragione per cui il SignInManager è stato implementato non può essere registrato per DI nello stesso modo.

Problemi correlati