Proteggere le applicazioni tramite l'autenticazione e l'autorizzazione

da Microsoft

Scarica il PDF

Questo è il passaggio 9 di un'esercitazione gratuita "NerdDinner" che illustra come creare un'applicazione Web di piccole dimensioni, ma completa usando ASP.NET MVC 1.

Il passaggio 9 illustra come aggiungere l'autenticazione e l'autorizzazione per proteggere l'applicazione NerdDinner, in modo che gli utenti debbano registrare e accedere al sito per creare nuove cene e solo l'utente che ospita una cena può modificarlo in un secondo momento.

Se si usa ASP.NET MVC 3, è consigliabile seguire le esercitazioni di Introduzione With MVC 3 o MVC Music Store.

Passaggio 9: Autenticazione e autorizzazione

A questo punto l'applicazione NerdDinner concede a chiunque visita il sito la possibilità di creare e modificare i dettagli di qualsiasi cena. Cambiamo questo in modo che gli utenti debbano registrare e accedere al sito per creare nuove cene e aggiungere una restrizione in modo che solo l'utente che ospita una cena possa modificarlo in un secondo momento.

Per abilitare questa operazione verrà usata l'autenticazione e l'autorizzazione per proteggere l'applicazione.

Informazioni sull'autenticazione e sull'autorizzazione

L'autenticazione è il processo di identificazione e convalida dell'identità di un client che accede a un'applicazione. Più semplicemente, si tratta di identificare "chi" l'utente finale è quando visitano un sito Web. ASP.NET supporta più modi per autenticare gli utenti del browser. Per le applicazioni Web Internet, l'approccio di autenticazione più comune usato è denominato "Autenticazione moduli". L'autenticazione di Forms consente a uno sviluppatore di creare un modulo di accesso HTML all'interno dell'applicazione e quindi convalidare il nome utente/password inviato da un utente finale in un database o in un altro archivio credenziali password. Se la combinazione nome utente/password è corretta, lo sviluppatore può quindi chiedere ASP.NET di inviare un cookie HTTP crittografato per identificare l'utente tra le richieste future. Verrà usata l'autenticazione dei moduli con l'applicazione NerdDinner.

L'autorizzazione è il processo per determinare se un utente autenticato ha l'autorizzazione per accedere a un determinato URL/risorsa o per eseguire un'azione. Ad esempio, all'interno dell'applicazione NerdDinner si vuole autorizzare che solo gli utenti che hanno effettuato l'accesso possono accedere a /Dinners/Create URL e creare nuove cene. Si vuole anche aggiungere la logica di autorizzazione in modo che solo l'utente che ospita una cena possa modificarlo e negare l'accesso alle modifiche a tutti gli altri utenti.

Autenticazione dei moduli e AccountController

Il modello di progetto di Visual Studio predefinito per ASP.NET MVC abilita automaticamente l'autenticazione dei moduli quando vengono create nuove applicazioni MVC ASP.NET. Aggiunge automaticamente anche un'implementazione predefinita della pagina di accesso dell'account al progetto, che semplifica l'integrazione della sicurezza all'interno di un sito.

La pagina master site.master predefinita visualizza un collegamento "Accesso" nella parte superiore destra del sito quando l'utente che accede non è autenticato:

Screenshot della pagina Nerd Dinner Host a Dinner .Screenshot of the Nerd Dinner Host a Dinner page. L'accesso è evidenziato nell'angolo superiore destro.

Facendo clic sul collegamento "Accesso" viene visualizzato un utente nell'URL /Account/LogOn :

Screenshot della pagina Accesso alla cena nerd.

I visitatori che non sono stati registrati possono farlo facendo clic sul collegamento "Registra", che li porterà all'URL /Account/ Registrare e consentire loro di immettere i dettagli dell'account:

Screenshot della pagina Nerd Dinner Create a New Account (Crea un nuovo account).

Facendo clic sul pulsante "Registra" verrà creato un nuovo utente all'interno del sistema di appartenenza ASP.NET e autenticare l'utente nel sito usando l'autenticazione dei moduli.

Quando un utente è connesso, il file Site.master modifica la parte superiore destra della pagina per restituire un messaggio "Benvenuto [nome utente]!" e esegue il rendering di un collegamento "Disconnessione" anziché "Accedi" uno. Facendo clic sul collegamento "Disconnessione" si disconnette l'utente:

Screenshot della pagina Nerd Dinner Host a Dinner form .Screenshot della pagina Nerd Dinner Host a Dinner Form. I pulsanti Benvenuto e Disconnessione sono evidenziati nell'angolo superiore destro.

La funzionalità di accesso, disconnessione e registrazione precedente viene implementata all'interno della classe AccountController aggiunta al progetto da Visual Studio quando ha creato il progetto. L'interfaccia utente per AccountController viene implementata usando i modelli di visualizzazione all'interno della directory \Views\Account:

Screenshot dell'albero di navigazione Nerd Dinner. Il punto c del controller di account è evidenziato. Vengono evidenziate anche le voci di menu e cartella account.

La classe AccountController usa il sistema di autenticazione ASP.NET Form per emettere cookie di autenticazione crittografati e l'API di appartenenza ASP.NET per archiviare e convalidare i nomi utente/password. L'API di appartenenza ASP.NET è estendibile e consente l'uso di qualsiasi archivio credenziali password. ASP.NET viene fornito con implementazioni del provider di appartenenza predefinite che archiviano nome utente/password all'interno di un database SQL o all'interno di Active Directory.

È possibile configurare il provider di appartenenza che l'applicazione NerdDinner deve usare aprendo il file "web.config" nella radice del progetto e cercando la <sezione di appartenenza> all'interno. Il web.config predefinito aggiunto quando il progetto è stato creato registra il provider di appartenenza SQL e lo configura per usare una stringa di connessione denominata "ApplicationServices" per specificare il percorso del database.

La stringa di connessione predefinita "ApplicationServices" (specificata nella sezione connectionStrings> del file web.config) è configurata per l'uso <di SQL Express. Punta a un database SQL Express denominato "ASPNETDB. MDF" nella directory "App_Data" dell'applicazione. Se il database non esiste la prima volta che l'API di appartenenza viene usata all'interno dell'applicazione, ASP.NET creerà automaticamente il database e eseguirà automaticamente il provisioning dello schema di database di appartenenza appropriato all'interno di esso:

Screenshot dell'albero di navigazione Nerd Dinner. I dati dell'app vengono espansi e viene selezionato un punto P NET D B punto M D F.

Se invece di usare SQL Express si vuole usare un'istanza SQL Server completa (o connettersi a un database remoto), è necessario aggiornare la stringa di connessione "ApplicationServices" all'interno del file web.config e assicurarsi che lo schema di appartenenza appropriato sia stato aggiunto al database a cui punta. È possibile eseguire l'utilità "aspnet_regsql.exe" all'interno della directory \Windows\Microsoft.NET\Framework\v2.0.50727\ per aggiungere lo schema appropriato per l'appartenenza e gli altri servizi applicazione ASP.NET a un database.

Autorizzazione dell'URL /Dinners/Create usando il filtro [Autorizza]

Non è stato necessario scrivere codice per abilitare un'implementazione sicura di autenticazione e gestione degli account per l'applicazione NerdDinner. Gli utenti possono registrare nuovi account con l'applicazione e accedere/disconnessione del sito.

Ora è possibile aggiungere la logica di autorizzazione all'applicazione e usare lo stato di autenticazione e il nome utente dei visitatori per controllare ciò che possono e non possono eseguire all'interno del sito. Iniziamo aggiungendo la logica di autorizzazione ai metodi di azione "Crea" della classe DinnersController. In particolare, è necessario che gli utenti che accedono a /Dinners/Create URL devono essere connessi. Se non sono connessi, verranno reindirizzati alla pagina di accesso in modo che possano accedere.

L'implementazione di questa logica è piuttosto semplice. Tutto ciò che dobbiamo fare consiste nell'aggiungere un attributo di filtro [Autorizza] ai metodi di azione Create, ad esempio:

//
// GET: /Dinners/Create

[Authorize]
public ActionResult Create() {
   ...
} 

//
// POST: /Dinners/Create

[AcceptVerbs(HttpVerbs.Post), Authorize]
public ActionResult Create(Dinner dinnerToCreate) {
   ...
}

ASP.NET MVC supporta la possibilità di creare "filtri di azione" che possono essere usati per implementare la logica riabilitabile che può essere applicata in modo dichiarativo ai metodi di azione. Il filtro [Autorizza] è uno dei filtri di azione predefiniti forniti da ASP.NET MVC e consente a uno sviluppatore di applicare in modo dichiarativo le regole di autorizzazione ai metodi di azione e alle classi controller.

Quando applicato senza parametri (come sopra) il filtro [Autorizza] impone che l'utente che effettua la richiesta del metodo azione deve essere connesso e reindirizzerà automaticamente il browser all'URL di accesso se non sono. Quando si esegue il reindirizzamento dell'URL richiesto originariamente viene passato come argomento querystring, ad esempio :/Account/LogOn? ReturnUrl=%2fDinners%2fCreate. AccountController reindirizzerà quindi l'utente all'URL richiesto originariamente dopo aver eseguito l'accesso.

Il filtro [Autorizza] supporta facoltativamente la possibilità di specificare una proprietà "Utenti" o "Ruoli" che può essere usata per richiedere che l'utente sia connesso e all'interno di un elenco di utenti consentiti o di un membro di un ruolo di sicurezza consentito. Ad esempio, il codice seguente consente solo a due utenti specifici, "scottgu" e "billg", di accedere all'URL /Dinners/Create:

[Authorize(Users="scottgu,billg")]
public ActionResult Create() {
    ...
}

L'incorporamento di nomi utente specifici all'interno del codice tende a essere piuttosto non gestibile anche se. Un approccio migliore consiste nel definire "ruoli" di livello superiore a cui il codice viene eseguito e quindi eseguire il mapping degli utenti al ruolo usando un database o un sistema active directory (consentendo l'archiviazione esterna dell'elenco di mapping utenti effettivo dal codice). ASP.NET include un'API di gestione dei ruoli predefinita e un set predefinito di provider di ruoli (inclusi quelli per SQL e Active Directory) che consentono di eseguire questo mapping utente/ruolo. È quindi possibile aggiornare il codice per consentire solo agli utenti all'interno di un ruolo "amministratore" specifico di accedere all'URL /Dinners/Create:

[Authorize(Roles="admin")]
public ActionResult Create() {
   ...
}

Uso della proprietà User.Identity.Name durante la creazione di cene

È possibile recuperare il nome utente dell'utente attualmente connesso di una richiesta usando la proprietà User.Identity.Name esposta nella classe base Controller.

In precedenza, quando è stata implementata la versione HTTP-POST del metodo di azione Create() è stato eseguito il hardcoded della proprietà "HostedBy" della proprietà Dinner in una stringa statica. È ora possibile aggiornare questo codice in modo che usi la proprietà User.Identity.Name, nonché aggiungere automaticamente un RSVP per l'host che crea la cena:

//
// POST: /Dinners/Create

[AcceptVerbs(HttpVerbs.Post), Authorize]
public ActionResult Create(Dinner dinner) {

    if (ModelState.IsValid) {
    
        try {
            dinner.HostedBy = User.Identity.Name;

            RSVP rsvp = new RSVP();
            rsvp.AttendeeName = User.Identity.Name;
            dinner.RSVPs.Add(rsvp);

            dinnerRepository.Add(dinner);
            dinnerRepository.Save();

            return RedirectToAction("Details", new { id=dinner.DinnerID });
        }
        catch {
            ModelState.AddModelErrors(dinner.GetRuleViolations());
        }
    }

    return View(new DinnerFormViewModel(dinner));
}

Poiché è stato aggiunto un attributo [Autorizza] al metodo Create(), ASP.NET MVC garantisce che il metodo di azione venga eseguito solo se l'utente che visita /Dinners/Create URL è connesso al sito. Di conseguenza, il valore della proprietà User.Identity.Name conterrà sempre un nome utente valido.

Uso della proprietà User.Identity.Name durante la modifica delle cene

È ora possibile aggiungere una logica di autorizzazione che limita gli utenti in modo che possano modificare solo le proprietà delle cene che si ospitano.

Per facilitare questa operazione, si aggiungerà prima un metodo helper "IsHostedBy(username)" all'oggetto Dinner (all'interno della classe parziale Dinner.cs compilata in precedenza). Questo metodo helper restituisce true o false a seconda che un nome utente specificato corrisponda alla proprietà Dinner HostedBy e incapsula la logica necessaria per eseguire un confronto tra stringhe senza distinzione tra maiuscole e minuscole:

public partial class Dinner {

    public bool IsHostedBy(string userName) {
        return HostedBy.Equals(userName, StringComparison.InvariantCultureIgnoreCase);
    }
}

Verrà quindi aggiunto un attributo [Autorizza] ai metodi di azione Edit() all'interno della classe DinnersController. In questo modo, gli utenti devono essere connessi per richiedere un URL /Dinners/Edit/[id].

È quindi possibile aggiungere codice ai metodi Di modifica che usano il metodo helper Dinner.IsHostedBy(username) per verificare che l'utente connesso corrisponda all'host Dinner. Se l'utente non è l'host, verrà visualizzata una visualizzazione "InvalidOwner" e terminare la richiesta. Il codice a cui eseguire questa operazione è simile al seguente:

//
// GET: /Dinners/Edit/5

[Authorize]
public ActionResult Edit(int id) {

    Dinner dinner = dinnerRepository.GetDinner(id);

    if (!dinner.IsHostedBy(User.Identity.Name))
        return View("InvalidOwner");

    return View(new DinnerFormViewModel(dinner));
}

//
// POST: /Dinners/Edit/5

[AcceptVerbs(HttpVerbs.Post), Authorize]
public ActionResult Edit(int id, FormCollection collection) {

    Dinner dinner = dinnerRepository.GetDinner(id);

    if (!dinner.IsHostedBy(User.Identity.Name))
        return View("InvalidOwner");

    try {
        UpdateModel(dinner);

        dinnerRepository.Save();

        return RedirectToAction("Details", new {id = dinner.DinnerID});
    }
    catch {
        ModelState.AddModelErrors(dinnerToEdit.GetRuleViolations());

        return View(new DinnerFormViewModel(dinner));
    }
}

È quindi possibile fare clic con il pulsante destro del mouse sulla directory \Views\Dinners e scegliere il comando di menu Aggiungi visualizzazione> per creare una nuova visualizzazione "InvalidOwner". Verrà popolato con il messaggio di errore seguente:

<asp:Content ID="Title" ContentPlaceHolderID="TitleContent" runat="server">
    You Don't Own This Dinner
</asp:Content>

<asp:Content ID="Main" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Error Accessing Dinner</h2>

    <p>Sorry - but only the host of a Dinner can edit or delete it.</p>

</asp:Content>

E ora quando un utente tenta di modificare una cena che non è proprietaria, riceverà un messaggio di errore:

Screenshot del messaggio di errore nella pagina Web Nerd Dinner.

È possibile ripetere gli stessi passaggi per i metodi di azione Delete() all'interno del controller per bloccare l'autorizzazione per eliminare anche Le cene e assicurarsi che solo l'host di una cena possa eliminarlo.

Il collegamento al metodo di azione Edit and Delete della classe DinnersController viene collegato all'URL dei dettagli:

Screenshot della pagina Cena nerd. I pulsanti Modifica ed Eliminazione vengono cerchiati nella parte inferiore. I dettagli U R L sono cerchiati nella parte superiore.

Attualmente vengono visualizzati i collegamenti modifica ed eliminazione, indipendentemente dal fatto che il visitatore dell'URL dei dettagli sia l'host della cena. Cambiamo questo in modo che i collegamenti vengano visualizzati solo se l'utente che visita è il proprietario della cena.

Il metodo di azione Details() all'interno di DinnersController recupera un oggetto Dinner e quindi lo passa come oggetto modello al modello di visualizzazione:

//
// GET: /Dinners/Details/5

public ActionResult Details(int id) {

    Dinner dinner = dinnerRepository.GetDinner(id);

    if (dinner == null)
        return View("NotFound");

    return View(dinner);
}

È possibile aggiornare il modello di visualizzazione per visualizzare/nascondere i collegamenti Modifica ed Elimina usando il metodo helper Dinner.IsHostedBy() come illustrato di seguito:

<% if (Model.IsHostedBy(Context.User.Identity.Name)) { %>

   <%= Html.ActionLink("Edit Dinner", "Edit", new { id=Model.DinnerID }) %> |
   <%= Html.ActionLink("Delete Dinner", "Delete", new {id=Model.DinnerID}) %>    

<% } %>

Passaggi successivi

Ora esaminiamo come è possibile abilitare gli utenti autenticati a RSVP per le cene usando AJAX.