Condividi tramite


Authentication and Authorization in ASP.NET Web API (in inglese)

di Rick Anderson

È stata creata un'API Web, ma ora si vuole controllare l'accesso. In questa serie di articoli verranno esaminate alcune opzioni per proteggere un'API Web da utenti non autorizzati. Questa serie illustra sia l'autenticazione che l'autorizzazione.

  • L'autenticazione conosce l'identità dell'utente. Ad esempio, Alice accede con il nome utente e la password e il server usa la password per autenticare Alice.
  • L'autorizzazione decide se un utente può eseguire un'azione. Ad esempio, Alice dispone dell'autorizzazione per ottenere una risorsa ma non per creare una risorsa.

Il primo articolo della serie offre una panoramica generale dell'autenticazione e dell'autorizzazione in API Web ASP.NET. Altri argomenti descrivono gli scenari di autenticazione comuni per l'API Web.

Nota

Grazie alle persone che hanno esaminato questa serie e fornito feedback prezioso: Rick Anderson, Levi Broderick, Barry Dorrans, Tom Dykstra, Hongmei Ge, David Matson, Daniel Roth, Tim Teebken.

Authentication

L'API Web presuppone che l'autenticazione venga eseguita nell'host. Per l'hosting Web, l'host è IIS, che usa i moduli HTTP per l'autenticazione. È possibile configurare il progetto in modo che usi uno qualsiasi dei moduli di autenticazione incorporati in IIS o ASP.NET oppure scrivere un modulo HTTP personalizzato per eseguire l'autenticazione personalizzata.

Quando l'host autentica l'utente, crea un'entità, ovvero un oggetto IPrincipal che rappresenta il contesto di sicurezza in cui è in esecuzione il codice. L'host collega l'entità al thread corrente impostando Thread.CurrentPrincipal. L'entità contiene un oggetto Identity associato che contiene informazioni sull'utente. Se l'utente è autenticato, la proprietà Identity.IsAuthenticated restituisce true. Per le richieste anonime, IsAuthenticated restituisce false. Per altre informazioni sulle entità di sicurezza, vedere Sicurezza basata sui ruoli.

Gestori di messaggi HTTP per l'autenticazione

Anziché usare l'host per l'autenticazione, è possibile inserire la logica di autenticazione in un gestore di messaggi HTTP. In tal caso, il gestore messaggi esamina la richiesta HTTP e imposta l'entità.

Quando è consigliabile usare i gestori di messaggi per l'autenticazione? Ecco alcuni compromessi:

  • Un modulo HTTP visualizza tutte le richieste che passano attraverso la pipeline ASP.NET. Un gestore di messaggi visualizza solo le richieste indirizzate all'API Web.
  • È possibile impostare gestori di messaggi per route, che consente di applicare uno schema di autenticazione a una route specifica.
  • I moduli HTTP sono specifici di IIS. I gestori di messaggi sono indipendenti dall'host, quindi possono essere usati sia con l'hosting Web che con l'self-hosting.
  • I moduli HTTP partecipano alla registrazione, al controllo e così via di IIS.
  • I moduli HTTP vengono eseguiti in precedenza nella pipeline. Se si gestisce l'autenticazione in un gestore di messaggi, l'entità non viene impostata fino all'esecuzione del gestore. Inoltre, l'entità torna all'entità precedente quando la risposta lascia il gestore messaggi.

In genere, se non è necessario supportare l'self-hosting, un modulo HTTP è un'opzione migliore. Se è necessario supportare l'self-hosting, prendere in considerazione un gestore di messaggi.

Impostazione dell'entità

Se l'applicazione esegue una logica di autenticazione personalizzata, è necessario impostare l'entità in due posizioni:

  • Thread.CurrentPrincipal. Questa proprietà è il modo standard per impostare l'entità del thread in .NET.
  • HttpContext.Current.User. Questa proprietà è specifica per ASP.NET.

Il codice seguente illustra come impostare l'entità:

private void SetPrincipal(IPrincipal principal)
{
    Thread.CurrentPrincipal = principal;
    if (HttpContext.Current != null)
    {
        HttpContext.Current.User = principal;
    }
}

Per l'hosting Web, è necessario impostare l'entità in entrambe le posizioni; in caso contrario, il contesto di sicurezza potrebbe diventare incoerente. Per l'self-hosting, tuttavia, HttpContext.Current è null. Per assicurarsi che il codice sia indipendente dall'host, verificare quindi la presenza di null prima di assegnare a HttpContext.Current, come illustrato.

Autorizzazione

L'autorizzazione viene eseguita più avanti nella pipeline, più vicina al controller. Ciò consente di effettuare scelte più granulari quando si concede l'accesso alle risorse.

  • I filtri di autorizzazione vengono eseguiti prima dell'azione del controller. Se la richiesta non è autorizzata, il filtro restituisce una risposta di errore e l'azione non viene richiamata.
  • All'interno di un'azione del controller è possibile ottenere l'entità corrente dalla proprietà ApiController.User . Ad esempio, è possibile filtrare un elenco di risorse in base al nome utente, restituendo solo le risorse che appartengono a tale utente.

Diagramma della pipeline di autenticazione e autorizzazione.

Uso dell'attributo [Authorize]

L'API Web fornisce un filtro di autorizzazione predefinito , AuthorizeAttribute. Questo filtro controlla se l'utente è autenticato. In caso contrario, restituisce il codice di stato HTTP 401 (Non autorizzato), senza richiamare l'azione.

È possibile applicare il filtro a livello globale, a livello di controller o a livello di singole azioni.

Globale: per limitare l'accesso per ogni controller API Web, aggiungere il filtro AuthorizeAttribute all'elenco di filtri globale:

public static void Register(HttpConfiguration config)
{
    config.Filters.Add(new AuthorizeAttribute());
}

Controller: per limitare l'accesso per un controller specifico, aggiungere il filtro come attributo al controller:

// Require authorization for all actions on the controller.
[Authorize]
public class ValuesController : ApiController
{
    public HttpResponseMessage Get(int id) { ... }
    public HttpResponseMessage Post() { ... }
}

Azione: per limitare l'accesso per azioni specifiche, aggiungere l'attributo al metodo action:

public class ValuesController : ApiController
{
    public HttpResponseMessage Get() { ... }

    // Require authorization for a specific action.
    [Authorize]
    public HttpResponseMessage Post() { ... }
}

In alternativa, è possibile limitare il controller e quindi consentire l'accesso anonimo a azioni specifiche usando l'attributo [AllowAnonymous] . Nell'esempio seguente il Post metodo è limitato, ma il metodo consente l'accesso Get anonimo.

[Authorize]
public class ValuesController : ApiController
{
    [AllowAnonymous]
    public HttpResponseMessage Get() { ... }

    public HttpResponseMessage Post() { ... }
}

Negli esempi precedenti il filtro consente a qualsiasi utente autenticato di accedere ai metodi con restrizioni; vengono mantenuti solo gli utenti anonimi. È anche possibile limitare l'accesso a utenti specifici o a utenti con ruoli specifici:

// Restrict by user:
[Authorize(Users="Alice,Bob")]
public class ValuesController : ApiController
{
}
   
// Restrict by role:
[Authorize(Roles="Administrators")]
public class ValuesController : ApiController
{
}

Nota

Il filtro AuthorizeAttribute per i controller API Web si trova nello spazio dei nomi System.Web.Http . Esiste un filtro simile per i controller MVC nello spazio dei nomi System.Web.Mvc , che non è compatibile con i controller API Web.

Filtri di autorizzazione personalizzati

Per scrivere un filtro di autorizzazione personalizzato, derivare da uno di questi tipi:

  • AuthorizeAttribute. Estendere questa classe per eseguire la logica di autorizzazione in base all'utente corrente e ai ruoli dell'utente.
  • AuthorizationFilterAttribute. Estendere questa classe per eseguire la logica di autorizzazione sincrona che non è necessariamente basata sull'utente o sul ruolo corrente.
  • IAuthorizationFilter. Implementare questa interfaccia per eseguire la logica di autorizzazione asincrona; ad esempio, se la logica di autorizzazione effettua chiamate asincrone di I/O o di rete. Se la logica di autorizzazione è associata alla CPU, è più semplice derivare da AuthorizationFilterAttribute, perché non è necessario scrivere un metodo asincrono.

Il diagramma seguente illustra la gerarchia di classi per la classe AuthorizeAttribute .

Diagramma della gerarchia di classi per la classe Authorize Attribute.

Diagramma della gerarchia di classi per la classe Authorize Attribute. Authorize Attribute è nella parte inferiore, con una freccia rivolta verso l'alto su Authorization Filter Attribute (Attributo filtro autorizzazione) e una freccia rivolta verso l'alto su I Authorization Filter (Filtro autorizzazione) nella parte superiore.

Autorizzazione all'interno di un'azione del controller

In alcuni casi, è possibile consentire a una richiesta di procedere, ma modificare il comportamento in base all'entità. Ad esempio, le informazioni restituite potrebbero cambiare a seconda del ruolo dell'utente. All'interno di un metodo controller è possibile ottenere l'entità corrente dalla proprietà ApiController.User .

public HttpResponseMessage Get()
{
    if (User.IsInRole("Administrators"))
    {
        // ...
    }
}