Sécuriser des applications avec l’authentification et l’autorisation

par Microsoft

Télécharger le PDF

Il s’agit de l’étape 9 d’un didacticiel gratuit sur l’application « NerdDinner » qui explique comment créer une application web petite mais complète à l’aide de ASP.NET MVC 1.

L’étape 9 montre comment ajouter l’authentification et l’autorisation pour sécuriser notre application NerdDinner, afin que les utilisateurs doivent s’inscrire et se connecter au site pour créer de nouveaux dîners, et seul l’utilisateur qui héberge un dîner peut le modifier ultérieurement.

Si vous utilisez ASP.NET MVC 3, nous vous recommandons de suivre les didacticiels Prise en main Avec MVC 3 ou MVC Music Store.

NerdDinner Étape 9 : Authentification et autorisation

À l’heure actuelle, notre application NerdDinner accorde à toute personne visitant le site la possibilité de créer et de modifier les détails de n’importe quel dîner. Nous allons changer cela afin que les utilisateurs doivent s’inscrire et se connecter au site pour créer de nouveaux dîners, et ajouter une restriction afin que seul l’utilisateur qui héberge un dîner puisse le modifier ultérieurement.

Pour activer cette option, nous allons utiliser l’authentification et l’autorisation pour sécuriser notre application.

Présentation de l’authentification et de l’autorisation

L’authentification est le processus d’identification et de validation de l’identité d’un client accédant à une application. Plus simplement, il s’agit d’identifier « qui » est l’utilisateur final lorsqu’il visite un site web. ASP.NET prend en charge plusieurs façons d’authentifier les utilisateurs du navigateur. Pour les applications web Internet, l’approche d’authentification la plus courante utilisée est appelée « authentification par formulaire ». L’authentification par formulaire permet à un développeur de créer un formulaire de connexion HTML dans son application, puis de valider le nom d’utilisateur/mot de passe qu’un utilisateur final envoie sur une base de données ou un autre magasin d’informations d’identification de mot de passe. Si la combinaison nom d’utilisateur/mot de passe est correcte, le développeur peut alors demander à ASP.NET d’émettre un cookie HTTP chiffré pour identifier l’utilisateur dans les requêtes futures. Nous utiliserons l’authentification par formulaire avec notre application NerdDinner.

L’autorisation est le processus qui consiste à déterminer si un utilisateur authentifié a l’autorisation d’accéder à une URL/ressource particulière ou d’effectuer une action. Par exemple, dans notre application NerdDinner, nous allons autoriser que seuls les utilisateurs connectés puissent accéder à l’URL /Dinners/Create et créer des dîners. Nous allons également ajouter une logique d’autorisation afin que seul l’utilisateur qui héberge un dîner puisse le modifier et refuser l’accès aux modifications à tous les autres utilisateurs.

Authentification par formulaire et AccountController

Le modèle de projet Visual Studio par défaut pour ASP.NET MVC active automatiquement l’authentification par formulaire lorsque de nouvelles applications ASP.NET MVC sont créées. Il ajoute également automatiquement une implémentation de page de connexion de compte prédéfinie au projet, ce qui facilite l’intégration de la sécurité au sein d’un site.

Site par défaut. master master page affiche un lien « Ouvrir une session » en haut à droite du site lorsque l’utilisateur qui y accède n’est pas authentifié :

Capture d’écran de la page Nerd Dinner Host a Dinner. L’option Ouvrir une session est mise en surbrillance dans le coin supérieur droit.

En cliquant sur le lien « Ouvrir une session », un utilisateur accède à l’URL /Account/LogOn :

Capture d’écran de la page Nerd Dinner Log On.

Les visiteurs qui ne se sont pas inscrits peuvent le faire en cliquant sur le lien « S’inscrire », qui les amène à l’URL /Account/Register et leur permet d’entrer les détails du compte :

Capture d’écran de la page Nerd Dinner Create a New Account.

Cliquez sur le bouton « Inscrire » pour créer un utilisateur dans le système d’appartenance ASP.NET et authentifier l’utilisateur sur le site à l’aide de l’authentification par formulaire.

Lorsqu’un utilisateur est connecté, le site. master modifie la partie supérieure droite de la page pour générer un message « Bienvenue [nom d’utilisateur] ! » et affiche un lien « Déconnexion » au lieu d’un lien « Ouvrir une session ». Le fait de cliquer sur le lien « Déconnexion » déconnecte l’utilisateur :

Capture d’écran de la page du formulaire Nerd Dinner Host a Dinner. Les boutons Bienvenue et Déconnexion sont mis en surbrillance dans le coin supérieur droit.

La fonctionnalité de connexion, de déconnexion et d’inscription ci-dessus est implémentée dans la classe AccountController qui a été ajoutée à notre projet par Visual Studio lors de la création du projet. L’interface utilisateur de AccountController est implémentée à l’aide de modèles d’affichage dans le répertoire \Views\Account :

Capture d’écran de l’arborescence de navigation Nerd Dinner. Le point c s du contrôleur de compte est mis en surbrillance. Les éléments de menu dossier et compte sont également mis en surbrillance.

La classe AccountController utilise le système d’authentification par formulaire ASP.NET pour émettre des cookies d’authentification chiffrés et l’API d’appartenance ASP.NET pour stocker et valider les noms d’utilisateur/mots de passe. L’API d’appartenance ASP.NET est extensible et permet d’utiliser n’importe quel magasin d’informations d’identification de mot de passe. ASP.NET est fourni avec des implémentations de fournisseurs d’appartenance intégrées qui stockent les noms d’utilisateur/mots de passe dans une base de données SQL ou dans Active Directory.

Nous pouvons configurer le fournisseur d’appartenances que notre application NerdDinner doit utiliser en ouvrant le fichier « web.config » à la racine du projet et en recherchant la <section d’appartenance> qu’il contient. La web.config par défaut ajoutée lors de la création du projet inscrit le fournisseur d’appartenances SQL et le configure pour utiliser une chaîne de connexion nommée « ApplicationServices » pour spécifier l’emplacement de la base de données.

La chaîne de connexion « ApplicationServices » par défaut (qui est spécifiée dans la <section connectionStrings> du fichier web.config) est configurée pour utiliser SQL Express. Il pointe vers une base de données SQL Express nommée « ASPNETDB. MDF » sous le répertoire « App_Data » de l’application. Si cette base de données n’existe pas la première fois que l’API d’appartenance est utilisée dans l’application, ASP.NET créez automatiquement la base de données et approvisionnez le schéma de base de données d’appartenance approprié dans celle-ci :

Capture d’écran de l’arborescence de navigation Nerd Dinner. Les données d’application sont développées et A S P NET D B dot MD F est sélectionné.

Si, au lieu d’utiliser SQL Express, nous voulions utiliser un SQL Server instance complet (ou nous connecter à une base de données distante), il nous suffit de mettre à jour la chaîne de connexion « ApplicationServices » dans le fichier web.config et de vérifier que le schéma d’appartenance approprié a été ajouté à la base de données vers laquelle elle pointe. Vous pouvez exécuter l’utilitaire « aspnet_regsql.exe » dans le répertoire \Windows\Microsoft.NET\Framework\v2.0.50727\ pour ajouter le schéma approprié pour l’appartenance et l’autre ASP.NET services d’application à une base de données.

Autorisation de l’URL /Dinners/Create à l’aide du filtre [Autoriser]

Nous n’avons pas eu à écrire de code pour activer une implémentation sécurisée de l’authentification et de la gestion des comptes pour l’application NerdDinner. Les utilisateurs peuvent inscrire de nouveaux comptes auprès de notre application et se connecter/se déconnecter du site.

Nous pouvons maintenant ajouter une logique d’autorisation à l’application et utiliser le status d’authentification et le nom d’utilisateur des visiteurs pour contrôler ce qu’ils peuvent ou ne peuvent pas faire dans le site. Commençons par ajouter une logique d’autorisation aux méthodes d’action « Créer » de notre classe DinnersController. Plus précisément, nous exigeons que les utilisateurs accédant à l’URL /Dinners/Create soient connectés. S’ils ne sont pas connectés, nous les redirigerons vers la page de connexion afin qu’ils puissent se connecter.

L’implémentation de cette logique est assez facile. Tout ce que nous devons faire est d’ajouter un attribut de filtre [Authorize] à nos méthodes d’action Create comme suit :

//
// GET: /Dinners/Create

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

//
// POST: /Dinners/Create

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

ASP.NET MVC prend en charge la possibilité de créer des « filtres d’action » qui peuvent être utilisés pour implémenter une logique réutilisable qui peut être appliquée de manière déclarative aux méthodes d’action. Le filtre [Autoriser] est l’un des filtres d’action intégrés fournis par ASP.NET MVC, et il permet à un développeur d’appliquer de manière déclarative des règles d’autorisation aux méthodes d’action et aux classes de contrôleur.

Lorsqu’il est appliqué sans aucun paramètre (comme ci-dessus), le filtre [Autoriser] applique que l’utilisateur qui effectue la demande de méthode d’action doit être connecté et redirige automatiquement le navigateur vers l’URL de connexion s’il ne le fait pas. Lorsque vous effectuez cette redirection, l’URL demandée à l’origine est passée en tant qu’argument de chaîne de requête (par exemple : /Account/LogOn ? ReturnUrl=%2fDinners%2fCreate). AccountController redirige ensuite l’utilisateur vers l’URL initialement demandée une fois qu’il s’est connecté.

Le filtre [Autoriser] prend éventuellement en charge la possibilité de spécifier une propriété « Utilisateurs » ou « Rôles » qui peut être utilisée pour exiger que l’utilisateur soit à la fois connecté et dans une liste d’utilisateurs autorisés ou d’un membre d’un rôle de sécurité autorisé. Par exemple, le code ci-dessous permet uniquement à deux utilisateurs spécifiques, « scottgu » et « billg », d’accéder à l’URL /Dinners/Create :

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

Cependant, l’incorporation de noms d’utilisateurs spécifiques dans le code a tendance à être assez peu gérable. Une meilleure approche consiste à définir des « rôles » de niveau supérieur que le code vérifie, puis à mapper les utilisateurs au rôle à l’aide d’une base de données ou d’un système Active Directory (permettant de stocker la liste de mappage d’utilisateurs réelle à partir du code). ASP.NET inclut une API de gestion des rôles intégrée ainsi qu’un ensemble intégré de fournisseurs de rôles (y compris ceux pour SQL et Active Directory) qui peuvent vous aider à effectuer ce mappage utilisateur/rôle. Nous pouvons ensuite mettre à jour le code pour autoriser uniquement les utilisateurs au sein d’un rôle « administrateur » spécifique à accéder à l’URL /Dinners/Create :

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

Utilisation de la propriété User.Identity.Name lors de la création de dîners

Nous pouvons récupérer le nom d’utilisateur de l’utilisateur actuellement connecté d’une requête à l’aide de la propriété User.Identity.Name exposée sur la classe de base du contrôleur.

Plus tôt, lorsque nous avons implémenté la version HTTP-POST de notre méthode d’action Create(), nous avions codé en dur la propriété « HostedBy » du Dinner en chaîne statique. Nous pouvons maintenant mettre à jour ce code pour utiliser la propriété User.Identity.Name et ajouter automatiquement une RSVP pour l’hôte qui crée le dîner :

//
// 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));
}

Étant donné que nous avons ajouté un attribut [Authorize] à la méthode Create(), ASP.NET MVC garantit que la méthode d’action s’exécute uniquement si l’utilisateur qui accède à l’URL /Dinners/Create est connecté sur le site. Par conséquent, la valeur de la propriété User.Identity.Name contient toujours un nom d’utilisateur valide.

Utilisation de la propriété User.Identity.Name lors de la modification de dîners

Nous allons maintenant ajouter une logique d’autorisation qui restreint les utilisateurs afin qu’ils puissent uniquement modifier les propriétés des dîners qu’ils hébergent eux-mêmes.

Pour vous aider, nous allons d’abord ajouter une méthode d’assistance « IsHostedBy(username) » à notre objet Dinner (dans la classe partielle Dinner.cs que nous avons créée précédemment). Cette méthode d’assistance retourne true ou false selon qu’un nom d’utilisateur fourni correspond à la propriété Dinner HostedBy et encapsule la logique nécessaire pour effectuer une comparaison de chaînes qui ne respecte pas la casse :

public partial class Dinner {

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

Nous allons ensuite ajouter un attribut [Authorize] aux méthodes d’action Edit() dans notre classe DinnersController. Cela garantit que les utilisateurs doivent être connectés pour demander une URL /Dinners/Edit/[id].

Nous pouvons ensuite ajouter du code à nos méthodes Edit qui utilise la méthode d’assistance Dinner.IsHostedBy(username) pour vérifier que l’utilisateur connecté correspond à l’hôte dinner. Si l’utilisateur n’est pas l’hôte, nous allons afficher une vue « InvalidOwner » et mettre fin à la demande. Le code pour effectuer cette opération ressemble à ce qui suit :

//
// 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));
    }
}

Nous pouvons ensuite cliquer avec le bouton droit sur le répertoire \Views\Dinners et choisir la commande de menu Add-View> pour créer une vue « InvalidOwner ». Nous allons le remplir avec le message d’erreur ci-dessous :

<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>

Et maintenant, lorsqu’un utilisateur tente de modifier un dîner dont il n’est pas propriétaire, il reçoit un message d’erreur :

Capture d’écran du message d’erreur sur la page web Nerd Dinner.

Nous pouvons répéter les mêmes étapes pour les méthodes d’action Delete() au sein de notre contrôleur afin de verrouiller l’autorisation de supprimer des dîners et de nous assurer que seul l’hôte d’un dîner peut le supprimer.

Nous sommes liés à la méthode d’action Modifier et Supprimer de notre classe DinnersController à partir de notre URL Details :

Capture d’écran de la page Nerd Dinner. Les boutons Modifier et Supprimer sont cerclé en bas. Les détails de l’U R L sont cerclé en haut.

Actuellement, nous affichons les liens d’action Modifier et Supprimer, que le visiteur de l’URL des détails soit l’hôte du dîner. Nous allons changer cela afin que les liens soient affichés uniquement si l’utilisateur visiteur est le propriétaire du dîner.

La méthode d’action Details() dans notre DinnersController récupère un objet Dinner, puis le transmet en tant qu’objet de modèle à notre modèle d’affichage :

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

public ActionResult Details(int id) {

    Dinner dinner = dinnerRepository.GetDinner(id);

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

    return View(dinner);
}

Nous pouvons mettre à jour notre modèle d’affichage pour afficher/masquer de manière conditionnelle les liens Modifier et Supprimer à l’aide de la méthode d’assistance Dinner.IsHostedBy() comme ci-dessous :

<% 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}) %>    

<% } %>

Étapes suivantes

Voyons maintenant comment nous pouvons autoriser les utilisateurs authentifiés à RSVP pour les dîners à l’aide d’AJAX.