2015-09-05 15 views
9

Sto utilizzando Microsoft Owin e ASP.NET WebApi per l'autenticazione e il processo di autorizzazione per la mia applicazione client. Anche il server di autenticazione è protetto da HTTPS. Ho letto un paio di articoli su come utilizzare Microsoft Owin, uno di loro, che ho scelto di implementare è: Token Based Authentication using ASP.NET Web API 2, Owin, and IdentityID applicazione problema per ogni tipo di applicazione client

Ci sono alcune differenze tra il mio progetto e che l'attuazione:

  1. ho bisogno di identificare il mio cliente nel caso in cui la richiesta venga inviata dalla mia applicazione sul telefono cellulare, non da altri dispositivi o strumenti come Fiddler. Penso che una delle opzioni potrebbe essere l'invio di un ID applicazione per ogni richiesta dall'applicazione mobile. Ma non so come e dove dovrei verificare le richieste nell'applicazione server di autenticazione. Questo è molto importante per la registrazione degli utenti:

    [AllowAnonymous] 
        [Route("Register")] 
        public async Task<IHttpActionResult> Register(UserModel userModel) 
        { 
         if (!ModelState.IsValid) 
         { 
          return BadRequest(ModelState); 
         } 
    
         IdentityResult result = await _repo.RegisterUser(userModel); 
    
         IHttpActionResult errorResult = GetErrorResult(result); 
    
         if (errorResult != null) 
         { 
          return errorResult; 
         } 
    
         return Ok(); 
        } 
    

    Non voglio lasciare i dispositivi inaffidabili, vale a dire i clienti, tranne l'applicazione mobile, chiamare questo metodo.

  2. Devo consentire agli utenti anonimi di acquistare alcuni prodotti dal sito Web, ma non so quale sia la prassi migliore per emettere un token per utenti anonimi senza eseguire l'autenticazione.

risposta

6

Se si desidera identificare il client e autorizzarlo, è possibile sovrascrivere il metodo ValidateClientAuthentication.

Nell'esempio di Taiseer aver collegato troverete un codice:

public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) 
{ 
    context.Validated(); 
} 

e una nota che dice:

Come si nota questa classe eredita dalla classe “OAuthAuthorizationServerProvider”, noi' hai ridefinito due metodi "ValidateClientAuthentication" e "GrantResourceOwnerCredentials". Il primo metodo è responsabile per la convalida del "Client", nel nostro caso abbiamo un solo client, quindi restituiremo sempre il suo validato con successo.

Se si desidera convalidare il client, è necessario inserire un po 'di logica.
Normalmente si passa un clientId e un clientSecret nell'intestazione della propria richiesta http, in modo da poter convalidare la richiesta del client con alcuni parametri del database, ad esempio.

public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) 
{ 
    string clientId = string.Empty; 
    string clientSecret = string.Empty; 

    if (!context.TryGetBasicCredentials(out clientId, out clientSecret)) 
    { 
     context.TryGetFormCredentials(out clientId, out clientSecret); 
    } 

    if (context.ClientId == null) 
    { 
     context.SetError("invalid_client", "Client credentials could not be retrieved through the Authorization header."); 
     context.Rejected(); 

     return; 
    } 

    try 
    { 
     // You're going to check the client's credentials on a database. 
     if (clientId == "MyApp" && clientSecret == "MySecret") 
     { 
      context.Validated(clientId); 
     } 
     else 
     { 
      // Client could not be validated. 
      context.SetError("invalid_client", "Client credentials are invalid."); 
      context.Rejected(); 
     } 
    } 
    catch (Exception ex) 
    { 
     string errorMessage = ex.Message; 
     context.SetError("server_error"); 
     context.Rejected(); 
    } 

    return; 
} 

Nell'esempio di cui sopra si cercherà di estrarre le credenziali client inviati nell'intestazione della richiesta:

if (!context.TryGetBasicCredentials(out clientId, out clientSecret)) 
{ 
    context.TryGetFormCredentials(out clientId, out clientSecret); 
} 

e convalidato:

// You're going to check the client's credentials on a database. 
if (clientId == "MyApp" && clientSecret == "MySecret") 
{ 
    context.Validated(clientId); 
} 

se il client invia un'intestazione di richiesta errata è necessario rifiutare la richiesta:

context.SetError("invalid_client", "Client credentials are invalid."); 
context.Rejected(); 

Il metodo ValidateClientAuthentication viene elaborato prima di GrantResourceOwnerCredentials. In questo modo è possibile estenderlo e passare a GrantResourceOwnerCredentials alcune informazioni aggiuntive necessarie.

In una delle mie applicazioni ho creato una classe:

class ApplicationClient 
{ 
    public string Id { get; set; } 
    public string Name { get; set; } 
    public string ClientSecretHash { get; set; } 
    public OAuthGrant AllowedGrant { get; set; } 
    public DateTimeOffset CreatedOn { get; set; } 
} 

che uso in ValidateClientAuthentication subito dopo ho controllato il clientId e il segreto sono ok:

if (clientId == "MyApp" && clientSecret == "MySecret") 
{ 
    ApplicationClient client = new ApplicationClient(); 
    client.Id = clientId; 
    client.AllowedGrant = OAuthGrant.ResourceOwner; 
    client.ClientSecretHash = new PasswordHasher().HashPassword("MySecret"); 
    client.Name = "My App"; 
    client.CreatedOn = DateTimeOffset.UtcNow; 

    context.OwinContext.Set<ApplicationClient>("oauth:client", client); 

    context.Validated(clientId); 
} 

Come potete vedere qui

context.OwinContext.Set<ApplicationClient>("oauth:client", client); 

Sto impostando una variabile Owin che posso leggere in seguito. Nella tua GrantResourceOwnerCredentials ora si può leggere la variabile in caso di necessità:

public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) 
{ 
    ApplicationClient client = context.OwinContext.Get<ApplicationClient>("oauth:client"); 
    ... 
} 

Ora, se si vuole recuperare il token portatore - che si sta andando da utilizzare per tutte le chiamate API sicure - è necessario codificare il clientId e clientSecret (base64) e passarlo nell'intestazione della richiesta:

una richiesta Ajax con jQuery sarebbe simile a questa:

var clientId = "MyApp"; 
var clientSecret = "MySecret"; 

var authorizationBasic = $.base64.btoa(clientId + ':' + clientSecret); 

$.ajax({ 
     type: 'POST', 
     url: '<your API token validator>', 
     data: { username: 'John', password: 'Smith', grant_type: 'password' }, 
     dataType: "json", 
     contentType: 'application/x-www-form-urlencoded; charset=utf-8', 
     xhrFields: { 
      withCredentials: true 
     }, 
     headers: { 
      'Authorization': 'Basic ' + authorizationBasic 
     }, 
     beforeSend: function (xhr) { 
     }, 
     success: function (result) { 
     var token = result.access_token; 
     }, 
     error: function (req, status, error) { 
      alert(error); 
     } 
}); 

come potete vedere ho anche aggiunto il nome utente e password - con il g rant tipo - nel corpo della richiesta:

data: { username: 'John', password: 'Smith', grant_type: 'password' } 

modo che il server sarà in grado di convalidare il cliente (clientId + clientSecret) e l'utente (username + password).

Se la richiesta è successo si dovrebbe tornare un gettone valida:

oAuth.Token = result.access_token; 

che è possibile memorizzare da qualche parte per le seguenti richieste.

Ora è possibile utilizzare questo token per tutte le richieste al api:

$.ajax({ 
    type: 'GET', 
    url: 'myapi/fetchCustomer/001', 
    data: { }, 
    dataType: "json", 
    headers: { 
    'Authorization': 'Bearer ' + oAuth.Token 
    }, 
    success: function (result) { 
    // your customer is in the result. 
    }, 
    error: function (req, status, error) { 
    alert(error); 
    } 
}); 

Un'altra cosa che si potrebbe desiderare di aggiungere alla vostra API durante l'avvio è SuppressDefaultHostAuthentication:

config.SuppressDefaultHostAuthentication(); 

questo è un metodo di estensione di HttpConfiguration. Poiché utilizzi token bearer, vuoi sopprimere il meccanismo di autenticazione standard basato su cookie.

Taiseer ha scritto un'altra serie di articles che vale la pena leggere dove spiega tutte queste cose.

Ho creato un github repo in cui è possibile vedere come funziona.
L'API Web è auto-ospitata e ci sono due client: jQuery e Applicazione Console.

+0

Che risposta perfetta e ben strutturata! Metterò alla prova la tua soluzione. Grazie. –

+0

Fammi sapere come va. Ho passato un po 'di tempo a cercare di capire come tutti questi pezzi combaciano. Non è stato facile, soprattutto perché non c'è molta documentazione online. – LeftyX

+0

Sì, hai ragione, ho passato due giorni solo per l'apprendimento della struttura a causa della mancanza di documentazione. Segnalerò il risultato :) –

Problemi correlati