2015-02-22 6 views
10

Ho un front-end Web che chiama un backend ASP Web Api 2. L'autenticazione è gestita con ASP Identity. Per alcuni dei controller che sto creando, devo conoscere l'utente che sta effettuando la chiamata. Non voglio dover creare un modello strano da includere nell'identità dell'utente (che non memorizzo nemmeno nel client).Come ottenere il contesto dell'utente durante le chiamate Web Api?

Tutte le chiamate all'API sono autorizzate utilizzando un token al portatore, il mio pensiero è che il controller dovrebbe essere in grado di determinare il contesto dell'utente in base a questo, ma non so come implementarlo. Ho cercato ma non so cosa sto cercando esattamente e non ho trovato nulla di rilevante. Vado a fare una cosa del genere ...

public async Task<IHttpActionResult> Post(ApplicationIdentity identity, WalkthroughModel data) 

Aggiornamento

ho trovato il sotto che sembrava molto promettente ... ma il valore è sempre nullo! Il mio controller eredita da ApiController e ha un'intestazione Autorizza.

var userid = User.Identity.GetUserId(); 

Update 2

Ho anche provato tutte le soluzioni in Get the current user, within an ApiController action, without passing the userID as a parameter ma nessuna funziona. Non importa quello che sto ottenendo un'identità che è valido e auth'd, ma ha un ID utente nullo

Update 3

Ecco dove sono a ora.

[Authorize] 
    [Route("Email")] 
    public async Task<IHttpActionResult> Get() 
    { 
     var testa = User.Identity.GetType(); 
     var testb = User.Identity.GetUserId(); 
     var testc = User.Identity.AuthenticationType; 
     var testd = User.Identity.IsAuthenticated; 


     return Ok(); 
    } 
testa = Name: ClaimsIdentity, 
testb = null, 
testc = Bearer, 
testd = true 

utente è autenticato, ovviamente, ma sono in grado di recuperare la loro userID.

Update 4

ho trovato una risposta, ma sono davvero infelice con esso ...

ClaimsIdentity identity = (ClaimsIdentity)User.Identity; 
string username = identity.Claims.First().Value; 

Questo mi fa il nome utente, senza alcuna chiamata db ma sembra molto janky e un dolore da sostenere in futuro. Mi piacerebbe se qualcuno avesse una risposta migliore.

Cosa devo fare se è necessario modificare le richieste inoltrate lungo la strada? Inoltre, ogni volta che ho effettivamente bisogno dell'ID dell'utente devo effettuare una chiamata db per convertire il nome utente nell'ID

+0

Così, quando si emette la richiesta perché non aggiungere uno per il nome utente? È inoltre possibile definire i propri metodi di estensione su ClaimsIdentity. – Casey

+0

@emodendroket Sembra una buona idea, ma non ho idea di come farlo. Sono piuttosto debole nei confronti dei membri, quindi sto lavorando su modelli/tutorial trovati online. Non esprimo esplicitamente un reclamo con il nome utente ma a quanto pare succede da qualche parte nel mio codice: - \ Grazie per il suggerimento Proverò a risolvere quello –

+0

@emodendroket Allo stesso tempo ... secondo tutte le mie ricerche User.Identity.GetUserId(); dovrebbe * funzionare e non lo è! Voglio capire perché non funziona e risolverlo nel modo giusto, o se non altro comprenderlo e implementare una soluzione alternativa. –

risposta

18

Un approccio comune consiste nel creare una classe base per i tuoi ApiControllers e sfruttare l'ApplicationUserManager per recuperare le informazioni necessarie. Con questo approccio, è possibile mantenere la logica per accedere alle informazioni dell'utente in un'unica posizione e riutilizzarle tra i controller.

public class BaseApiController : ApiController 
{ 
     private ApplicationUser _member; 

     public ApplicationUserManager UserManager 
     { 
      get { return HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>(); } 
     } 

     public string UserIdentityId 
     { 
      get 
      { 
       var user = UserManager.FindByName(User.Identity.Name); 
       return user.Id; 
      } 
     } 

     public ApplicationUser UserRecord 
     { 
      get 
      { 
       if (_member != null) 
       { 
        return _member ; 
       } 
       _member = UserManager.FindByEmail(Thread.CurrentPrincipal.Identity.Name); 
       return _member ; 
      } 
      set { _member = value; } 
     } 
} 
+0

Prestare attenzione alle prestazioni di UserManager.FindByX se si utilizza una versione leggermente più vecchia di AspNet Identity http://stackoverflow.com/questions/28346479/how-to-handle-5-million-users-asp-net-identity – Dunc

+1

È possibile voglio aggiungere la parola chiave 'abstract' a questa classe, per proteggerla dall'instradamento automatico dell'API Web e perché non dovrebbe esserci un'istanza di questa classe direttamente. – emackey

1

Io uso l'autenticazione utente personalizzata (Non uso AspIdentity perché i miei campi della tabella utente esistenti era molto diverso da proprietà IdentityUser) e creare ClaimsIdentity passando mio tavolo UserID e UserName di convalidare il mio gettone portatore sulle chiamate API.

public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) 
    { 
     User user; 
     try 
     { 
      var scope = Autofac.Integration.Owin.OwinContextExtensions.GetAutofacLifetimeScope(context.OwinContext); 
      _service = scope.Resolve<IUserService>(); 

      user = await _service.FindUserAsync(context.UserName); 

      if (user?.HashedPassword != Helpers.CustomPasswordHasher.GetHashedPassword(context.Password, user?.Salt)) 
      { 
       context.SetError("invalid_grant", "The user name or password is incorrect."); 
       return; 
      } 
     } 
     catch (Exception ex) 
     { 
      context.SetError("invalid_grant", ex.Message); 
      return; 
     } 

     var properties = new Dictionary<string, string>() 
     { 
      { ClaimTypes.NameIdentifier, user.UserID.ToString() }, 
      { ClaimTypes.Name, context.UserName } 
     }; 

     var identity = new ClaimsIdentity(context.Options.AuthenticationType); 
     properties.ToList().ForEach(c => identity.AddClaim(new Claim(c.Key, c.Value))); 

     var ticket = new AuthenticationTicket(identity, new AuthenticationProperties(properties)); 

     context.Validated(ticket); 
     context.Request.Context.Authentication.SignIn(identity); 
    } 

E come io uso il ClaimsIdentity per recuperare il mio utente dettagli tavolo su dettagli utente ApiController chiamano.

[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)] 
    [Route("Details")] 
    public async Task<IHttpActionResult> Details() 
    { 
     var user = await _service.GetAsync(RequestContext.Principal.Identity.GetUserId<int>()); 
     var basicDetails = Mapper.Map<User, BasicUserModel>(user); 

     return Ok(basicDetails); 
    } 

Avviso del ClaimTypes.NameIdentifier = GetUserID() e ClaimTypes.Name = GetUserName()