13

Ho bisogno di creare un'applicazione C# API Web per un database MySQL esistente. Sono riuscito a utilizzare Entity Framework 6 per associare ogni tabella di database a un'API RESTful (che consente le operazioni CRUD).DB: prima confusione dell'autenticazione con l'API Web ASP.NET 2 + EF6

Desidero implementare un sistema di login/registrazione (in modo da poter implementare ruoli e autorizzazioni in futuro e limitare alcune richieste API).

Il database MySQL devo usare ha un tavolo per gli utenti (chiamati user) che ha le seguenti autoesplicativi colonne:

  • id
  • email
  • username
  • password_hash

Sembra che lo standard de facto per l'autenticazione sia ASP.Net Identity. Ho passato l'ultima ora a cercare di capire come far funzionare Identity con un setup DB-First Entity Framework esistente.

Se provo a costruire ApplicationUser casi memorizzazione user casi (entità dal database MySQL) per recuperare i dati degli utenti, ottengo il seguente errore:

The entity type ApplicationUser is not part of the model for the current context.

presumo che ho bisogno di memorizzare i dati di identità in il mio database MySQL, ma non ho trovato alcuna risorsa su come farlo. Ho provato a rimuovere completamente la classe ApplicationUser e la mia classe di entità user deriva da IdentityUser, ma la chiamata a UserManager.CreateAsync ha provocato errori di conversione da LINQ a entità.

Come si configura l'autenticazione in un'applicazione Web API 2, con un'entità user esistente?

risposta

19

Tu dici:

I want to implement a login/registration system (so that I can implement roles and permissions in the future, and restrict certain API requests).

How do I setup authentication in a Web API 2 application, having an existing user entity?

Significa sicuramente che si DO NOT bisogno di ASP.NET Identità. ASP.NET Identity è una tecnologia per gestire tutti gli elementi degli utenti. In realtà non "crea" il meccanismo di autenticazione. L'identità di ASP.NET utilizza il meccanismo di autenticazione OWIN, che è un'altra cosa.

Quello che state cercando non è "come utilizzare ASP.NET identità con la mia tabella esistente Users", ma "Come configurare l'autenticazione OWIN usando la mia tabella utenti esistenti"

Per utilizzare OWIN auth attenersi alla seguente procedura:

installare i pacchetti:

Owin 
Microsoft.AspNet.Cors 
Microsoft.AspNet.WebApi.Client 
Microsoft.AspNet.WebApi.Core 
Microsoft.AspNet.WebApi.Owin 
Microsoft.AspNet.WebApi.WebHost 
Microsoft.Owin 
Microsoft.Owin.Host.SystemWeb 
Microsoft.Owin.Security 
Microsoft.Owin.Security.OAuth 

Creare Startup.cs di file all'interno della cartella principale (examp le):

assicurarsi che [assembly: OwinStartup] sia configurato correttamente

[assembly: OwinStartup(typeof(YourProject.Startup))] 
namespace YourProject 
{ 
    public class Startup 
    { 
     public void Configuration(IAppBuilder app) 
     { 
      var config = new HttpConfiguration(); 
      //other configurations 

      ConfigureOAuth(app); 
      app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll); 
      app.UseWebApi(config); 
     } 

     public void ConfigureOAuth(IAppBuilder app) 
     { 
      var oAuthServerOptions = new OAuthAuthorizationServerOptions() 
      { 
       AllowInsecureHttp = true, 
       TokenEndpointPath = new PathString("/api/security/token"), 
       AccessTokenExpireTimeSpan = TimeSpan.FromHours(2), 
       Provider = new AuthorizationServerProvider() 
      }; 

      app.UseOAuthAuthorizationServer(oAuthServerOptions); 
      app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()); 
     } 
    } 

    public class AuthorizationServerProvider : OAuthAuthorizationServerProvider 
    { 
     public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) 
     { 
      context.Validated(); 
     } 

     public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) 
     { 
      context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" }); 

      try 
      { 
       //retrieve your user from database. ex: 
       var user = await userService.Authenticate(context.UserName, context.Password); 

       var identity = new ClaimsIdentity(context.Options.AuthenticationType); 

       identity.AddClaim(new Claim(ClaimTypes.Name, user.Name)); 
       identity.AddClaim(new Claim(ClaimTypes.Email, user.Email)); 

       //roles example 
       var rolesTechnicalNamesUser = new List<string>(); 

       if (user.Roles != null) 
       { 
        rolesTechnicalNamesUser = user.Roles.Select(x => x.TechnicalName).ToList(); 

        foreach (var role in user.Roles) 
         identity.AddClaim(new Claim(ClaimTypes.Role, role.TechnicalName)); 
       } 

       var principal = new GenericPrincipal(identity, rolesTechnicalNamesUser.ToArray()); 

       Thread.CurrentPrincipal = principal; 

       context.Validated(identity); 
      } 
      catch (Exception ex) 
      { 
       context.SetError("invalid_grant", "message"); 
      } 
     } 
    } 
} 

Utilizzare l'attributo [Authorize] per autorizzare le azioni.

chiamata api/security/token con GrantType, UserName, e Password per ottenere il token portatore. Come questo:

"grant_type=password&username=" + username + "&password=" password; 

Invia il token all'interno del HttpHeader Authorization come Bearer "YOURTOKENHERE". Come questo:

headers: { 'Authorization': 'Bearer ' + token } 

Spero che aiuti!

+0

Grazie, questo è quello che stavo cercando. Scusa se la mia domanda non è chiara, ma sono stato confuso dal "ruolo" di ASP.NET Identity. –

+0

Sei il benvenuto amico. Se hai problemi nell'implementazione, non esitare a commentare qui –

+1

Ehi, potresti dirmi quale sarebbe l'oggetto userService nel tuo codice sopra? Solo un oggetto di classe personalizzato che si connetterebbe al mio DB Contesto e restituire il mio Utente personalizzato dal DB? L'utente non deve ereditare dopo IUser o qualcosa del genere? – Savail

7

Dal momento che lo schema DB non sono compatibili con l'impostazione predefinita UserStore È necessario implementare le proprie UserStore e UserPasswordStore classi poi iniettare a UserManager. Considerate questo semplice esempio:

Scrivi la classe utente personalizzata e implementare IUser interfaccia:

class User:IUser<int> 
{ 
    public int ID {get;set;} 
    public string Username{get;set;} 
    public string Password_hash {get;set;} 
    // some other properties 
} 

Ora all'autore vostra abitudine UserStore e IUserPasswordStore classe come questa:

public class MyUserStore : IUserStore<User>, IUserPasswordStore<User> 
{ 
    private readonly MyDbContext _context; 

    public MyUserStore(MyDbContext context) 
    { 
     _context=context; 
    } 

    public Task CreateAsync(AppUser user) 
    { 
     // implement your desired logic such as 
     // _context.Users.Add(user); 
    } 

    public Task DeleteAsync(AppUser user) 
    { 
     // implement your desired logic 
    } 

    public Task<AppUser> FindByIdAsync(string userId) 
    { 
     // implement your desired logic 
    } 

    public Task<AppUser> FindByNameAsync(string userName) 
    { 
     // implement your desired logic 
    } 

    public Task UpdateAsync(AppUser user) 
    { 
     // implement your desired logic 
    } 

    public void Dispose() 
    { 
     // implement your desired logic 
    } 

    // Following 3 methods are needed for IUserPasswordStore 
    public Task<string> GetPasswordHashAsync(AppUser user) 
    { 
     // something like this: 
     return Task.FromResult(user.Password_hash); 
    } 

    public Task<bool> HasPasswordAsync(AppUser user) 
    { 
     return Task.FromResult(user.Password_hash != null); 
    } 

    public Task SetPasswordHashAsync(AppUser user, string passwordHash) 
    { 
     user.Password_hash = passwordHash; 
     return Task.FromResult(0); 
    } 
} 

Ora avete molto proprio user store semplicemente iniettarlo al gestore utenti:

public class ApplicationUserManager: UserManager<User, int> 
{ 
    public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context) 
    { 
     var manager = new ApplicationUserManager(new MyUserStore(context.Get<MyDbContext>())); 
     // rest of code 
    } 
} 

Si noti inoltre che è necessario ereditare direttamente la classe Contesto DB da DbContext non IdentityDbContext poiché è stato implementato il proprio archivio utenti.