Créer une application ASP.NET Core avec des données utilisateur protégées par une autorisationCreate an ASP.NET Core app with user data protected by authorization

Par Rick Anderson et Joe AudetteBy Rick Anderson and Joe Audette

Consultez ce fichier PDF pour la version d’ASP.NET Core MVC.See this PDF for the ASP.NET Core MVC version. La version d’ASP.NET Core 1.1 de ce didacticiel est dans cela dossier.The ASP.NET Core 1.1 version of this tutorial is in this folder. L’exemple ASP.NET Core 1.1 du exemples.The 1.1 ASP.NET Core sample is in the samples.

Consultez ce fichier pdfSee this pdf

Ce didacticiel montre comment créer une application web ASP.NET Core avec des données utilisateur protégées par une autorisation.This tutorial shows how to create an ASP.NET Core web app with user data protected by authorization. Il affiche une liste de contacts (inscrits) les utilisateurs authentifiés ont créés.It displays a list of contacts that authenticated (registered) users have created. Il existe trois groupes de sécurité :There are three security groups:

  • Utilisateurs inscrits peut afficher toutes les données approuvées et peuvent modifier ou supprimer leurs propres données.Registered users can view all the approved data and can edit/delete their own data.
  • Gestionnaires de peuvent approuver ou rejeter des données de contact.Managers can approve or reject contact data. Seuls les contacts approuvés sont visibles aux utilisateurs.Only approved contacts are visible to users.
  • Les administrateurs peut approuver/rejeter et modifier ou de supprimer toutes les données.Administrators can approve/reject and edit/delete any data.

Exactement les images dans ce document ne correspondent pas les derniers modèles.The images in this document exactly don't match the latest templates.

Dans l’image suivante, l’utilisateur Rick (rick@example.com) n’est connecté.In the following image, user Rick (rick@example.com) is signed in. Rick peut uniquement afficher les contacts approuvés et modifier/supprimer/créer un nouveau liens pour ses contacts.Rick can only view approved contacts and Edit/Delete/Create New links for his contacts. Seul le dernier enregistrement créé par Rick, affiche modifier et supprimer des liens.Only the last record, created by Rick, displays Edit and Delete links. Autres utilisateurs ne voient le dernier enregistrement jusqu'à ce qu’un gestionnaire ou un administrateur modifie le statut « Approved ».Other users won't see the last record until a manager or administrator changes the status to "Approved".

Capture d’écran montrant Rick connecté

Dans l’image suivante, manager@contoso.com est signé dans et dans le rôle :In the following image, manager@contoso.com is signed in and in the manager's role:

Capture d’écran manager@contoso.com connecté

L’illustration suivante montre les gestionnaires de vue des détails d’un contact :The following image shows the managers details view of a contact:

Vue du responsable d’un contact

Le approuver et rejeter boutons sont affichés uniquement pour les responsables et les administrateurs.The Approve and Reject buttons are only displayed for managers and administrators.

Dans l’image suivante, admin@contoso.com est signé dans et dans le rôle de l’administrateur :In the following image, admin@contoso.com is signed in and in the administrator's role:

Capture d’écran admin@contoso.com connecté

L’administrateur a tous les privilèges.The administrator has all privileges. Elle peut lire/modifier/supprimer un contact et modifier l’état de contacts.She can read/edit/delete any contact and change the status of contacts.

L’application a été créée par la structure suit Contact modèle :The app was created by scaffolding the following Contact model:

public class Contact
{
    public int ContactId { get; set; }
    public string Name { get; set; }
    public string Address { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Zip { get; set; }
    [DataType(DataType.EmailAddress)]
    public string Email { get; set; }
}

L’exemple contient les gestionnaires d’autorisation suivants :The sample contains the following authorization handlers:

  • ContactIsOwnerAuthorizationHandler: Garantit qu’un utilisateur peut modifier uniquement leurs données.ContactIsOwnerAuthorizationHandler: Ensures that a user can only edit their data.
  • ContactManagerAuthorizationHandler: Permet aux gestionnaires d’approuver ou rejeter des contacts.ContactManagerAuthorizationHandler: Allows managers to approve or reject contacts.
  • ContactAdministratorsAuthorizationHandler: Permet aux administrateurs d’approuver ou rejeter des contacts et à modifier/supprimer des contacts.ContactAdministratorsAuthorizationHandler: Allows administrators to approve or reject contacts and to edit/delete contacts.

PrérequisPrerequisites

Ce didacticiel est avancé.This tutorial is advanced. Vous devez être familiarisé avec :You should be familiar with:

Le démarrage et l’application terminéeThe starter and completed app

Télécharger le terminé application.Download the completed app. Test l’application terminée afin de vous familiariser avec ses fonctionnalités de sécurité.Test the completed app so you become familiar with its security features.

L’application de démarrageThe starter app

Télécharger le starter application.Download the starter app.

Exécutez l’application, appuyez sur la ContactManager lien et vérifiez que vous pouvez créer, modifier et supprimer un contact.Run the app, tap the ContactManager link, and verify you can create, edit, and delete a contact.

Sécuriser les données utilisateurSecure user data

Les sections suivantes ont toutes les principales étapes pour créer l’application de données utilisateur sécurisée.The following sections have all the major steps to create the secure user data app. Il peut s’avérer utile pour faire référence au projet terminé.You may find it helpful to refer to the completed project.

Lier les données de contact à l’utilisateurTie the contact data to the user

Utilisez ASP.NET identité ID d’utilisateur pour garantir les utilisateurs permettre modifier leurs données, mais pas d’autres données utilisateurs.Use the ASP.NET Identity user ID to ensure users can edit their data, but not other users data. Ajouter OwnerID et ContactStatus à la Contact modèle :Add OwnerID and ContactStatus to the Contact model:

public class Contact
{
    public int ContactId { get; set; }

    // user ID from AspNetUser table.
    public string OwnerID { get; set; }

    public string Name { get; set; }
    public string Address { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Zip { get; set; }
    [DataType(DataType.EmailAddress)]
    public string Email { get; set; }

    public ContactStatus Status { get; set; }
}

public enum ContactStatus
{
    Submitted,
    Approved,
    Rejected
}

OwnerID est l’ID d’utilisateur à partir de la AspNetUser table dans le identité base de données.OwnerID is the user's ID from the AspNetUser table in the Identity database. Le Status champ détermine si un contact est visible par les utilisateurs généraux.The Status field determines if a contact is viewable by general users.

Créer une nouvelle migration et mettre à jour de la base de données :Create a new migration and update the database:

dotnet ef migrations add userID_Status
dotnet ef database update

Ajouter des services de rôle à l’identitéAdd Role services to Identity

Ajouter fonctionnalité Ajouter des rôles pour ajouter des services de rôle :Append AddRoles to add Role services:

public void ConfigureServices(IServiceCollection services)
{

    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDefaultIdentity<IdentityUser>(
        options => options.SignIn.RequireConfirmedAccount = true)
        .AddRoles<IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>();

Demander aux utilisateurs authentifiésRequire authenticated users

Définir la stratégie d’authentification par défaut pour exiger l’authentification des utilisateurs :Set the default authentication policy to require users to be authenticated:

public void ConfigureServices(IServiceCollection services)
{

    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDefaultIdentity<IdentityUser>(
        options => options.SignIn.RequireConfirmedAccount = true)
        .AddRoles<IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>();

    services.AddRazorPages();

    services.AddControllers(config =>
    {
        // using Microsoft.AspNetCore.Mvc.Authorization;
        // using Microsoft.AspNetCore.Authorization;
        var policy = new AuthorizationPolicyBuilder()
                         .RequireAuthenticatedUser()
                         .Build();
        config.Filters.Add(new AuthorizeFilter(policy));
    });

Vous pouvez refuser l’authentification au niveau de la méthode Page Razor, de contrôleur ou d’action avec la [AllowAnonymous] attribut.You can opt out of authentication at the Razor Page, controller, or action method level with the [AllowAnonymous] attribute. Définition de la stratégie d’authentification par défaut pour les utilisateurs doivent être authentifiés protège nouvellement ajouté les Pages Razor et les contrôleurs.Setting the default authentication policy to require users to be authenticated protects newly added Razor Pages and controllers. Avec l’authentification requise par défaut est plus sécurisée que de s’appuyer sur de nouveaux contrôleurs et les Pages Razor pour inclure le [Authorize] attribut.Having authentication required by default is more secure than relying on new controllers and Razor Pages to include the [Authorize] attribute.

Ajouter AllowAnonymous vers les pages d’Index et de confidentialité pour les utilisateurs anonymes peuvent obtenir des informations sur le site avant ils s’inscrivent.Add AllowAnonymous to the Index and Privacy pages so anonymous users can get information about the site before they register.

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;

namespace ContactManager.Pages
{
    [AllowAnonymous]
    public class IndexModel : PageModel
    {
        private readonly ILogger<IndexModel> _logger;

        public IndexModel(ILogger<IndexModel> logger)
        {
            _logger = logger;
        }

        public void OnGet()
        {

        }
    }
}

Configurer le compte de testConfigure the test account

Le SeedData classe crée deux comptes : administrateur et gestionnaire.The SeedData class creates two accounts: administrator and manager. Utilisez le outil Secret Manager pour définir un mot de passe pour ces comptes.Use the Secret Manager tool to set a password for these accounts. Définir le mot de passe à partir du répertoire de projet (le répertoire contenant Program.cs) :Set the password from the project directory (the directory containing Program.cs):

dotnet user-secrets set SeedUserPW <PW>

Si un mot de passe n’est pas spécifié, une exception est levée lorsque SeedData.Initialize est appelée.If a strong password is not specified, an exception is thrown when SeedData.Initialize is called.

Mise à jour Main à utiliser le mot de passe de test :Update Main to use the test password:

public class Program
{
    public static void Main(string[] args)
    {
        var host = CreateHostBuilder(args).Build();

        using (var scope = host.Services.CreateScope())
        {
            var services = scope.ServiceProvider;

            try
            {
                var context = services.GetRequiredService<ApplicationDbContext>();
                context.Database.Migrate();

                // requires using Microsoft.Extensions.Configuration;
                var config = host.Services.GetRequiredService<IConfiguration>();
                // Set password with the Secret Manager tool.
                // dotnet user-secrets set SeedUserPW <pw>

                var testUserPw = config["SeedUserPW"];

                SeedData.Initialize(services, testUserPw).Wait();
            }
            catch (Exception ex)
            {
                var logger = services.GetRequiredService<ILogger<Program>>();
                logger.LogError(ex, "An error occurred seeding the DB.");
            }
        }

        host.Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

Créez les comptes de test et de mettre à jour les contactsCreate the test accounts and update the contacts

Mise à jour le Initialize méthode dans la SeedData classe pour créer les comptes de test :Update the Initialize method in the SeedData class to create the test accounts:

public static async Task Initialize(IServiceProvider serviceProvider, string testUserPw)
{
    using (var context = new ApplicationDbContext(
        serviceProvider.GetRequiredService<DbContextOptions<ApplicationDbContext>>()))
    {
        // For sample purposes seed both with the same password.
        // Password is set with the following:
        // dotnet user-secrets set SeedUserPW <pw>
        // The admin user can do anything

        var adminID = await EnsureUser(serviceProvider, testUserPw, "admin@contoso.com");
        await EnsureRole(serviceProvider, adminID, Constants.ContactAdministratorsRole);

        // allowed user can create and edit contacts that they create
        var managerID = await EnsureUser(serviceProvider, testUserPw, "manager@contoso.com");
        await EnsureRole(serviceProvider, managerID, Constants.ContactManagersRole);

        SeedDB(context, adminID);
    }
}

private static async Task<string> EnsureUser(IServiceProvider serviceProvider,
                                            string testUserPw, string UserName)
{
    var userManager = serviceProvider.GetService<UserManager<IdentityUser>>();

    var user = await userManager.FindByNameAsync(UserName);
    if (user == null)
    {
        user = new IdentityUser {
            UserName = UserName,
            EmailConfirmed = true
        };
        await userManager.CreateAsync(user, testUserPw);
    }

    if (user == null)
    {
        throw new Exception("The password is probably not strong enough!");
    }

    return user.Id;
}

private static async Task<IdentityResult> EnsureRole(IServiceProvider serviceProvider,
                                                              string uid, string role)
{
    IdentityResult IR = null;
    var roleManager = serviceProvider.GetService<RoleManager<IdentityRole>>();

    if (roleManager == null)
    {
        throw new Exception("roleManager null");
    }

    if (!await roleManager.RoleExistsAsync(role))
    {
        IR = await roleManager.CreateAsync(new IdentityRole(role));
    }

    var userManager = serviceProvider.GetService<UserManager<IdentityUser>>();

    var user = await userManager.FindByIdAsync(uid);

    if(user == null)
    {
        throw new Exception("The testUserPw password was probably not strong enough!");
    }
    
    IR = await userManager.AddToRoleAsync(user, role);

    return IR;
}

Ajouter l’ID d’utilisateur administrateur et ContactStatus aux contacts.Add the administrator user ID and ContactStatus to the contacts. Rendre des contacts « Envoyé » et un « rejeté ».Make one of the contacts "Submitted" and one "Rejected". Ajoutez l’ID d’utilisateur et l’état pour tous les contacts.Add the user ID and status to all the contacts. Un seul contact est affiché :Only one contact is shown:

public static void SeedDB(ApplicationDbContext context, string adminID)
{
    if (context.Contact.Any())
    {
        return;   // DB has been seeded
    }

    context.Contact.AddRange(
        new Contact
        {
            Name = "Debra Garcia",
            Address = "1234 Main St",
            City = "Redmond",
            State = "WA",
            Zip = "10999",
            Email = "debra@example.com",
            Status = ContactStatus.Approved,
            OwnerID = adminID
        },

Créer le propriétaire, le gestionnaire et gestionnaires d’autorisations d’administrateurCreate owner, manager, and administrator authorization handlers

Créer un ContactIsOwnerAuthorizationHandler classe dans le autorisation dossier.Create a ContactIsOwnerAuthorizationHandler class in the Authorization folder. Le ContactIsOwnerAuthorizationHandler vérifie que l’utilisateur agissant sur une ressource propriétaire de la ressource.The ContactIsOwnerAuthorizationHandler verifies that the user acting on a resource owns the resource.

using ContactManager.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Infrastructure;
using Microsoft.AspNetCore.Identity;
using System.Threading.Tasks;

namespace ContactManager.Authorization
{
    public class ContactIsOwnerAuthorizationHandler
                : AuthorizationHandler<OperationAuthorizationRequirement, Contact>
    {
        UserManager<IdentityUser> _userManager;

        public ContactIsOwnerAuthorizationHandler(UserManager<IdentityUser> 
            userManager)
        {
            _userManager = userManager;
        }

        protected override Task
            HandleRequirementAsync(AuthorizationHandlerContext context,
                                   OperationAuthorizationRequirement requirement,
                                   Contact resource)
        {
            if (context.User == null || resource == null)
            {
                return Task.CompletedTask;
            }

            // If not asking for CRUD permission, return.

            if (requirement.Name != Constants.CreateOperationName &&
                requirement.Name != Constants.ReadOperationName   &&
                requirement.Name != Constants.UpdateOperationName &&
                requirement.Name != Constants.DeleteOperationName )
            {
                return Task.CompletedTask;
            }

            if (resource.OwnerID == _userManager.GetUserId(context.User))
            {
                context.Succeed(requirement);
            }

            return Task.CompletedTask;
        }
    }
}

Le ContactIsOwnerAuthorizationHandler appels contexte. Réussir si l’utilisateur authentifié actuel est le propriétaire du contact.The ContactIsOwnerAuthorizationHandler calls context.Succeed if the current authenticated user is the contact owner. Les gestionnaires d’autorisation généralement :Authorization handlers generally:

  • Retourner context.Succeed lorsque les conditions sont remplies.Return context.Succeed when the requirements are met.
  • Retourner Task.CompletedTask lorsque les conditions ne sont pas remplies.Return Task.CompletedTask when requirements aren't met. Task.CompletedTask n’est pas le succès ou l’échec—permet d’autres gestionnaires d’autorisation Exécuter.Task.CompletedTask is not success or failure—it allows other authorization handlers to run.

Si vous devez explicitement échouer, retourner contexte. Échec.If you need to explicitly fail, return context.Fail.

L’application permet aux propriétaires de contact de modifier, supprimer ou créer leurs propres données.The app allows contact owners to edit/delete/create their own data. ContactIsOwnerAuthorizationHandler n’a pas besoin vérifier l’opération passée dans le paramètre de condition.ContactIsOwnerAuthorizationHandler doesn't need to check the operation passed in the requirement parameter.

Créer un gestionnaire d’autorisation de gestionnaireCreate a manager authorization handler

Créer un ContactManagerAuthorizationHandler classe dans le autorisation dossier.Create a ContactManagerAuthorizationHandler class in the Authorization folder. Le ContactManagerAuthorizationHandler vérifie l’utilisateur agissant sur la ressource est un gestionnaire.The ContactManagerAuthorizationHandler verifies the user acting on the resource is a manager. Seuls les responsables peuvent approuver ou rejeter les modifications de contenu (nouvelle ou modifiées).Only managers can approve or reject content changes (new or changed).

using System.Threading.Tasks;
using ContactManager.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Infrastructure;
using Microsoft.AspNetCore.Identity;

namespace ContactManager.Authorization
{
    public class ContactManagerAuthorizationHandler :
        AuthorizationHandler<OperationAuthorizationRequirement, Contact>
    {
        protected override Task
            HandleRequirementAsync(AuthorizationHandlerContext context,
                                   OperationAuthorizationRequirement requirement,
                                   Contact resource)
        {
            if (context.User == null || resource == null)
            {
                return Task.CompletedTask;
            }

            // If not asking for approval/reject, return.
            if (requirement.Name != Constants.ApproveOperationName &&
                requirement.Name != Constants.RejectOperationName)
            {
                return Task.CompletedTask;
            }

            // Managers can approve or reject.
            if (context.User.IsInRole(Constants.ContactManagersRole))
            {
                context.Succeed(requirement);
            }

            return Task.CompletedTask;
        }
    }
}

Créer un gestionnaire d’autorisations d’administrateurCreate an administrator authorization handler

Créer un ContactAdministratorsAuthorizationHandler classe dans le autorisation dossier.Create a ContactAdministratorsAuthorizationHandler class in the Authorization folder. Le ContactAdministratorsAuthorizationHandler vérifie que l’utilisateur agissant sur la ressource est un administrateur.The ContactAdministratorsAuthorizationHandler verifies the user acting on the resource is an administrator. Administrateur peut effectuer toutes les opérations.Administrator can do all operations.

using System.Threading.Tasks;
using ContactManager.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Infrastructure;

namespace ContactManager.Authorization
{
    public class ContactAdministratorsAuthorizationHandler
                    : AuthorizationHandler<OperationAuthorizationRequirement, Contact>
    {
        protected override Task HandleRequirementAsync(
                                              AuthorizationHandlerContext context,
                                    OperationAuthorizationRequirement requirement, 
                                     Contact resource)
        {
            if (context.User == null)
            {
                return Task.CompletedTask;
            }

            // Administrators can do anything.
            if (context.User.IsInRole(Constants.ContactAdministratorsRole))
            {
                context.Succeed(requirement);
            }

            return Task.CompletedTask;
        }
    }
}

Inscrire les gestionnaires d’autorisationRegister the authorization handlers

Services à l’aide d’Entity Framework Core doivent être inscrit pour l’injection de dépendances à l’aide de AddScoped.Services using Entity Framework Core must be registered for dependency injection using AddScoped. Le ContactIsOwnerAuthorizationHandler utilise ASP.NET Core identité, qui est basé sur Entity Framework Core.The ContactIsOwnerAuthorizationHandler uses ASP.NET Core Identity, which is built on Entity Framework Core. Enregistrer les gestionnaires avec la collection de service afin qu’elles soient disponibles pour le ContactsController via l’injection de dépendances.Register the handlers with the service collection so they're available to the ContactsController through dependency injection. Ajoutez le code suivant à la fin de ConfigureServices:Add the following code to the end of ConfigureServices:

public void ConfigureServices(IServiceCollection services)
{

    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDefaultIdentity<IdentityUser>(
        options => options.SignIn.RequireConfirmedAccount = true)
        .AddRoles<IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>();

    services.AddRazorPages();

    services.AddControllers(config =>
    {
        // using Microsoft.AspNetCore.Mvc.Authorization;
        // using Microsoft.AspNetCore.Authorization;
        var policy = new AuthorizationPolicyBuilder()
                         .RequireAuthenticatedUser()
                         .Build();
        config.Filters.Add(new AuthorizeFilter(policy));
    });
    // Authorization handlers.
    services.AddScoped<IAuthorizationHandler,
                          ContactIsOwnerAuthorizationHandler>();

    services.AddSingleton<IAuthorizationHandler,
                          ContactAdministratorsAuthorizationHandler>();

    services.AddSingleton<IAuthorizationHandler,
                          ContactManagerAuthorizationHandler>();
}

ContactAdministratorsAuthorizationHandler et ContactManagerAuthorizationHandler sont ajoutés en tant que singletons.ContactAdministratorsAuthorizationHandler and ContactManagerAuthorizationHandler are added as singletons. Ils sont des singletons, car ils n’utilisent pas EF et toutes les informations nécessaires sont dans le Context paramètre de la HandleRequirementAsync (méthode).They're singletons because they don't use EF and all the information needed is in the Context parameter of the HandleRequirementAsync method.

Autorisation de la prise en chargeSupport authorization

Dans cette section, vous mettez à jour les Pages Razor et ajoutez une classe de configuration requise des opérations.In this section, you update the Razor Pages and add an operations requirements class.

Passez en revue la classe de configuration requise de contact des opérationsReview the contact operations requirements class

Examinez la ContactOperations classe.Review the ContactOperations class. Cette classe contient les exigences de l’application prend en charge :This class contains the requirements the app supports:

using Microsoft.AspNetCore.Authorization.Infrastructure;

namespace ContactManager.Authorization
{
    public static class ContactOperations
    {
        public static OperationAuthorizationRequirement Create =   
          new OperationAuthorizationRequirement {Name=Constants.CreateOperationName};
        public static OperationAuthorizationRequirement Read = 
          new OperationAuthorizationRequirement {Name=Constants.ReadOperationName};  
        public static OperationAuthorizationRequirement Update = 
          new OperationAuthorizationRequirement {Name=Constants.UpdateOperationName}; 
        public static OperationAuthorizationRequirement Delete = 
          new OperationAuthorizationRequirement {Name=Constants.DeleteOperationName};
        public static OperationAuthorizationRequirement Approve = 
          new OperationAuthorizationRequirement {Name=Constants.ApproveOperationName};
        public static OperationAuthorizationRequirement Reject = 
          new OperationAuthorizationRequirement {Name=Constants.RejectOperationName};
    }

    public class Constants
    {
        public static readonly string CreateOperationName = "Create";
        public static readonly string ReadOperationName = "Read";
        public static readonly string UpdateOperationName = "Update";
        public static readonly string DeleteOperationName = "Delete";
        public static readonly string ApproveOperationName = "Approve";
        public static readonly string RejectOperationName = "Reject";

        public static readonly string ContactAdministratorsRole = 
                                                              "ContactAdministrators";
        public static readonly string ContactManagersRole = "ContactManagers";
    }
}

Créer une classe de base pour les Pages Razor de ContactsCreate a base class for the Contacts Razor Pages

Créer une classe de base qui contient les services utilisés dans les Pages Razor de contacts.Create a base class that contains the services used in the contacts Razor Pages. La classe de base place le code d’initialisation dans un emplacement :The base class puts the initialization code in one location:

using ContactManager.Data;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace ContactManager.Pages.Contacts
{
    public class DI_BasePageModel : PageModel
    {
        protected ApplicationDbContext Context { get; }
        protected IAuthorizationService AuthorizationService { get; }
        protected UserManager<IdentityUser> UserManager { get; }

        public DI_BasePageModel(
            ApplicationDbContext context,
            IAuthorizationService authorizationService,
            UserManager<IdentityUser> userManager) : base()
        {
            Context = context;
            UserManager = userManager;
            AuthorizationService = authorizationService;
        } 
    }
}

Le code précédent :The preceding code:

  • Ajoute le IAuthorizationService service d’accéder aux gestionnaires d’autorisation.Adds the IAuthorizationService service to access to the authorization handlers.
  • Ajoute l’identité UserManager service.Adds the Identity UserManager service.
  • Ajoutez la ApplicationDbContext.Add the ApplicationDbContext.

Mettre à jour le CreateModelUpdate the CreateModel

Mettre à jour le constructeur de modèle de page de création à utiliser le DI_BasePageModel classe de base :Update the create page model constructor to use the DI_BasePageModel base class:

public class CreateModel : DI_BasePageModel
{
    public CreateModel(
        ApplicationDbContext context,
        IAuthorizationService authorizationService,
        UserManager<IdentityUser> userManager)
        : base(context, authorizationService, userManager)
    {
    }

Mise à jour le CreateModel.OnPostAsync méthode à :Update the CreateModel.OnPostAsync method to:

  • Ajoutez l’ID utilisateur pour la Contact modèle.Add the user ID to the Contact model.
  • Appeler le Gestionnaire d’autorisation pour vérifier que l’utilisateur est autorisé à créer des contacts.Call the authorization handler to verify the user has permission to create contacts.
public async Task<IActionResult> OnPostAsync()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    Contact.OwnerID = UserManager.GetUserId(User);

    // requires using ContactManager.Authorization;
    var isAuthorized = await AuthorizationService.AuthorizeAsync(
                                                User, Contact,
                                                ContactOperations.Create);
    if (!isAuthorized.Succeeded)
    {
        return new ChallengeResult();
    }

    Context.Contact.Add(Contact);
    await Context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Mettre à jour le IndexModelUpdate the IndexModel

Mise à jour le OnGetAsync méthode approuvées uniquement les contacts sont présentés aux utilisateurs généraux :Update the OnGetAsync method so only approved contacts are shown to general users:

public class IndexModel : DI_BasePageModel
{
    public IndexModel(
        ApplicationDbContext context,
        IAuthorizationService authorizationService,
        UserManager<IdentityUser> userManager)
        : base(context, authorizationService, userManager)
    {
    }

    public IList<Contact> Contact { get; set; }

    public async Task OnGetAsync()
    {
        var contacts = from c in Context.Contact
                       select c;

        var isAuthorized = User.IsInRole(Constants.ContactManagersRole) ||
                           User.IsInRole(Constants.ContactAdministratorsRole);

        var currentUserId = UserManager.GetUserId(User);

        // Only approved contacts are shown UNLESS you're authorized to see them
        // or you are the owner.
        if (!isAuthorized)
        {
            contacts = contacts.Where(c => c.Status == ContactStatus.Approved
                                        || c.OwnerID == currentUserId);
        }

        Contact = await contacts.ToListAsync();
    }
}

Mettre à jour le EditModelUpdate the EditModel

Ajoutez un gestionnaire d’autorisation pour vérifier que l’utilisateur propriétaire du contact.Add an authorization handler to verify the user owns the contact. Étant donné que l’autorisation de ressource est en cours de validation, le [Authorize] attribut n’est pas suffisant.Because resource authorization is being validated, the [Authorize] attribute is not enough. L’application n’a pas accès à la ressource lors de l’évaluation des attributs.The app doesn't have access to the resource when attributes are evaluated. Autorisation basée sur la ressource doit être impérative.Resource-based authorization must be imperative. Vérifications doivent être effectuées une fois que l’application a accès à la ressource, en le chargeant dans le modèle de page ou en le chargeant dans le gestionnaire lui-même.Checks must be performed once the app has access to the resource, either by loading it in the page model or by loading it within the handler itself. Vous accédez fréquemment la ressource en passant la clé de ressource.You frequently access the resource by passing in the resource key.

public class EditModel : DI_BasePageModel
{
    public EditModel(
        ApplicationDbContext context,
        IAuthorizationService authorizationService,
        UserManager<IdentityUser> userManager)
        : base(context, authorizationService, userManager)
    {
    }

    [BindProperty]
    public Contact Contact { get; set; }

    public async Task<IActionResult> OnGetAsync(int id)
    {
        Contact = await Context.Contact.FirstOrDefaultAsync(
                                             m => m.ContactId == id);

        if (Contact == null)
        {
            return NotFound();
        }

        var isAuthorized = await AuthorizationService.AuthorizeAsync(
                                                  User, Contact,
                                                  ContactOperations.Update);
        if (!isAuthorized.Succeeded)
        {
            return new ChallengeResult();
        }

        return Page();
    }

    public async Task<IActionResult> OnPostAsync(int id)
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        // Fetch Contact from DB to get OwnerID.
        var contact = await Context
            .Contact.AsNoTracking()
            .FirstOrDefaultAsync(m => m.ContactId == id);

        if (contact == null)
        {
            return NotFound();
        }

        var isAuthorized = await AuthorizationService.AuthorizeAsync(
                                                 User, contact,
                                                 ContactOperations.Update);
        if (!isAuthorized.Succeeded)
        {
            return new ChallengeResult();
        }

        Contact.OwnerID = contact.OwnerID;

        Context.Attach(Contact).State = EntityState.Modified;

        if (contact.Status == ContactStatus.Approved)
        {
            // If the contact is updated after approval, 
            // and the user cannot approve,
            // set the status back to submitted so the update can be
            // checked and approved.
            var canApprove = await AuthorizationService.AuthorizeAsync(User,
                                    contact,
                                    ContactOperations.Approve);

            if (!canApprove.Succeeded)
            {
                contact.Status = ContactStatus.Submitted;
            }
        }

        await Context.SaveChangesAsync();

        return RedirectToPage("./Index");
    }

    private bool ContactExists(int id)
    {
        return Context.Contact.Any(e => e.ContactId == id);
    }
}

Mettre à jour le DeleteModelUpdate the DeleteModel

Mettre à jour le modèle de page delete pour utiliser le Gestionnaire d’autorisation pour vérifier que l’utilisateur a l’autorisation de suppression sur le contact.Update the delete page model to use the authorization handler to verify the user has delete permission on the contact.

public class DeleteModel : DI_BasePageModel
{
    public DeleteModel(
        ApplicationDbContext context,
        IAuthorizationService authorizationService,
        UserManager<IdentityUser> userManager)
        : base(context, authorizationService, userManager)
    {
    }

    [BindProperty]
    public Contact Contact { get; set; }

    public async Task<IActionResult> OnGetAsync(int id)
    {
        Contact = await Context.Contact.FirstOrDefaultAsync(
                                             m => m.ContactId == id);

        if (Contact == null)
        {
            return NotFound();
        }

        var isAuthorized = await AuthorizationService.AuthorizeAsync(
                                                 User, Contact,
                                                 ContactOperations.Delete);
        if (!isAuthorized.Succeeded)
        {
            return new ChallengeResult();
        }

        return Page();
    }

    public async Task<IActionResult> OnPostAsync(int id)
    {
        Contact = await Context.Contact.FindAsync(id);

        var contact = await Context
            .Contact.AsNoTracking()
            .FirstOrDefaultAsync(m => m.ContactId == id);

        if (contact == null)
        {
            return NotFound();
        }

        var isAuthorized = await AuthorizationService.AuthorizeAsync(
                                                 User, contact,
                                                 ContactOperations.Delete);
        if (!isAuthorized.Succeeded)
        {
            return new ChallengeResult();
        }

        Context.Contact.Remove(Contact);
        await Context.SaveChangesAsync();

        return RedirectToPage("./Index");
    }
}

Injecter le service d’autorisation dans les vuesInject the authorization service into the views

Actuellement, le montre l’interface utilisateur modifie et supprime des liens pour les contacts de que l’utilisateur ne peut pas modifier.Currently, the UI shows edit and delete links for contacts the user can't modify.

Injecter le service d’autorisation dans le pages/_viewimports.cshtml afin qu’il soit disponible pour toutes les vues de fichiers :Inject the authorization service in the Pages/_ViewImports.cshtml file so it's available to all views:

@using Microsoft.AspNetCore.Identity
@using ContactManager
@using ContactManager.Data
@namespace ContactManager.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@using ContactManager.Authorization;
@using Microsoft.AspNetCore.Authorization
@using ContactManager.Models
@inject IAuthorizationService AuthorizationService

Le balisage précédent ajoute plusieurs using instructions.The preceding markup adds several using statements.

Mise à jour le modifier et supprimer lie dans Pages/Contacts/Index.cshtml afin d’être affichées uniquement pour les utilisateurs disposant des autorisations appropriées :Update the Edit and Delete links in Pages/Contacts/Index.cshtml so they're only rendered for users with the appropriate permissions:

@page
@model ContactManager.Pages.Contacts.IndexModel

@{
    ViewData["Title"] = "Index";
}

<h2>Index</h2>

<p>
    <a asp-page="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Contact[0].Name)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Contact[0].Address)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Contact[0].City)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Contact[0].State)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Contact[0].Zip)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Contact[0].Email)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Contact[0].Status)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model.Contact)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.Name)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Address)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.City)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.State)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Zip)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Email)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Status)
                </td>
                <td>
                    @if ((await AuthorizationService.AuthorizeAsync(
                     User, item,
                     ContactOperations.Update)).Succeeded)
                    {
                        <a asp-page="./Edit" asp-route-id="@item.ContactId">Edit</a>
                        <text> | </text>
                    }

                    <a asp-page="./Details" asp-route-id="@item.ContactId">Details</a>

                    @if ((await AuthorizationService.AuthorizeAsync(
                     User, item,
                     ContactOperations.Delete)).Succeeded)
                    {
                        <text> | </text>
                        <a asp-page="./Delete" asp-route-id="@item.ContactId">Delete</a>
                    }
                </td>
            </tr>
        }
    </tbody>
</table>

Avertissement

Masquage des liens à partir des utilisateurs qui ne sont pas autorisés à modifier les données ne sécuriser l’application.Hiding links from users that don't have permission to change data doesn't secure the app. Masquage des liens rend l’application plus conviviale en affichant les liens ne sont valides.Hiding links makes the app more user-friendly by displaying only valid links. Les utilisateurs peuvent hack l’URL générées pour appeler modifier et supprimer des opérations sur les données qu’ils ne possèdent pas.Users can hack the generated URLs to invoke edit and delete operations on data they don't own. Le contrôleur ou une Page Razor doit appliquer les vérifications d’accès pour sécuriser les données.The Razor Page or controller must enforce access checks to secure the data.

Détails de la mise à jourUpdate Details

Mettre à jour l’affichage des détails pour les responsables peuvent approuver ou rejeter des contacts :Update the details view so managers can approve or reject contacts:

        @*Precedng markup omitted for brevity.*@
        <dt>
            @Html.DisplayNameFor(model => model.Contact.Email)
        </dt>
        <dd>
            @Html.DisplayFor(model => model.Contact.Email)
        </dd>
        <dt>
            @Html.DisplayNameFor(model => model.Contact.Status)
        </dt>
        <dd>
            @Html.DisplayFor(model => model.Contact.Status)
        </dd>
    </dl>
</div>

@if (Model.Contact.Status != ContactStatus.Approved)
{
    @if ((await AuthorizationService.AuthorizeAsync(
     User, Model.Contact, ContactOperations.Approve)).Succeeded)
    {
        <form style="display:inline;" method="post">
            <input type="hidden" name="id" value="@Model.Contact.ContactId" />
            <input type="hidden" name="status" value="@ContactStatus.Approved" />
            <button type="submit" class="btn btn-xs btn-success">Approve</button>
        </form>
    }
}

@if (Model.Contact.Status != ContactStatus.Rejected)
{
    @if ((await AuthorizationService.AuthorizeAsync(
     User, Model.Contact, ContactOperations.Reject)).Succeeded)
    {
        <form style="display:inline;" method="post">
            <input type="hidden" name="id" value="@Model.Contact.ContactId" />
            <input type="hidden" name="status" value="@ContactStatus.Rejected" />
            <button type="submit" class="btn btn-xs btn-success">Reject</button>
        </form>
    }
}

<div>
    @if ((await AuthorizationService.AuthorizeAsync(
         User, Model.Contact,
         ContactOperations.Update)).Succeeded)
    {
        <a asp-page="./Edit" asp-route-id="@Model.Contact.ContactId">Edit</a>
        <text> | </text>
    }
    <a asp-page="./Index">Back to List</a>
</div>

Mettre à jour le modèle de page de détails :Update the details page model:

public class DetailsModel : DI_BasePageModel
{
    public DetailsModel(
        ApplicationDbContext context,
        IAuthorizationService authorizationService,
        UserManager<IdentityUser> userManager)
        : base(context, authorizationService, userManager)
    {
    }

    public Contact Contact { get; set; }

    public async Task<IActionResult> OnGetAsync(int id)
    {
        Contact = await Context.Contact.FirstOrDefaultAsync(m => m.ContactId == id);

        if (Contact == null)
        {
            return NotFound();
        }

        var isAuthorized = User.IsInRole(Constants.ContactManagersRole) ||
                           User.IsInRole(Constants.ContactAdministratorsRole);

        var currentUserId = UserManager.GetUserId(User);

        if (!isAuthorized 
            &&  currentUserId != Contact.OwnerID
            && Contact.Status != ContactStatus.Approved) 
        {
            return new ChallengeResult();
        }

        return Page();
    }

    public async Task<IActionResult> OnPostAsync(int id, ContactStatus status)
    {
        var contact = await Context.Contact.FirstOrDefaultAsync(
                                                  m => m.ContactId == id);

        if (contact == null)
        {
            return NotFound();
        }

        var contactOperation = (status == ContactStatus.Approved)
                                                   ? ContactOperations.Approve
                                                   : ContactOperations.Reject;

        var isAuthorized = await AuthorizationService.AuthorizeAsync(User, contact,
                                    contactOperation);
        if (!isAuthorized.Succeeded)
        {
            return new ChallengeResult();
        }
        contact.Status = status;
        Context.Contact.Update(contact);
        await Context.SaveChangesAsync();

        return RedirectToPage("./Index");
    }
}

Ajouter ou supprimer un utilisateur à un rôleAdd or remove a user to a role

Consultez ce problème pour plus d’informations sur :See this issue for information on:

  • Suppression de privilèges à partir d’un utilisateur.Removing privileges from a user. Désactivation par exemple, un utilisateur dans une application de conversation.For example, muting a user in a chat app.
  • Ajout des privilèges à un utilisateur.Adding privileges to a user.

Tester l’application terminéeTest the completed app

Si vous n’avez pas déjà défini un mot de passe pour les comptes d’utilisateur amorcée, utilisez le outil Secret Manager pour définir un mot de passe :If you haven't already set a password for seeded user accounts, use the Secret Manager tool to set a password:

  • Choisissez un mot de passe fort : Utiliser huit ou plus de caractères et au moins un caractère majuscule, numéro et de symboles.Choose a strong password: Use eight or more characters and at least one upper-case character, number, and symbol. Par exemple, Passw0rd! répond aux exigences de mot de passe fort.For example, Passw0rd! meets the strong password requirements.

  • Exécutez la commande suivante à partir du dossier du projet, où <PW> est le mot de passe :Execute the following command from the project's folder, where <PW> is the password:

    dotnet user-secrets set SeedUserPW <PW>
    

Si l’application a des contacts :If the app has contacts:

  • Supprimer tous les enregistrements dans la Contact table.Delete all of the records in the Contact table.
  • Redémarrez l’application pour amorcer la base de données.Restart the app to seed the database.

Un moyen simple de tester l’application terminée consiste à lancer les trois différents navigateurs (ou incognito/InPrivate sessions).An easy way to test the completed app is to launch three different browsers (or incognito/InPrivate sessions). Dans un navigateur, inscrire un nouvel utilisateur (par exemple, test@example.com).In one browser, register a new user (for example, test@example.com). Connectez-vous à chaque navigateur avec un autre utilisateur.Sign in to each browser with a different user. Vérifiez les opérations suivantes :Verify the following operations:

  • Les utilisateurs inscrits peuvent afficher toutes les données de contact approuvées.Registered users can view all of the approved contact data.
  • Les utilisateurs inscrits peuvent modifier ou supprimer leurs propres données.Registered users can edit/delete their own data.
  • Gestionnaires peuvent approuver/rejeter les données de contact.Managers can approve/reject contact data. Le Details afficher montre approuver et rejeter boutons.The Details view shows Approve and Reject buttons.
  • Les administrateurs peuvent approuver/rejeter et modifier ou de supprimer toutes les données.Administrators can approve/reject and edit/delete all data.
UtilisateurUser Amorcée par l’applicationSeeded by the app OptionsOptions
test@example.com NonNo Modifier/supprimer les données propres.Edit/delete the own data.
manager@contoso.com OuiYes Approuver/rejeter et modifier ou de supprimer des données propres.Approve/reject and edit/delete own data.
admin@contoso.com OuiYes Approuver/rejeter et de modifier ou de supprimer toutes les données.Approve/reject and edit/delete all data.

Créer un contact dans le navigateur de l’administrateur.Create a contact in the administrator's browser. Copiez l’URL pour supprimer et modifier à partir du contact de l’administrateur.Copy the URL for delete and edit from the administrator contact. Collez ces liens dans le navigateur de l’utilisateur de test pour vérifier que l’utilisateur de test ne peut pas effectuer ces opérations.Paste these links into the test user's browser to verify the test user can't perform these operations.

Créer l’application de démarrageCreate the starter app

  • Créer une application Pages Razor nommée « ContactManager »Create a Razor Pages app named "ContactManager"

    • Créer l’application avec comptes d’utilisateur individuels.Create the app with Individual User Accounts.
    • Nommez-Le « ContactManager » afin de l’espace de noms correspond à l’espace de noms utilisé dans l’exemple.Name it "ContactManager" so the namespace matches the namespace used in the sample.
    • -uld Spécifie la base de données locale au lieu de SQLite-uld specifies LocalDB instead of SQLite
    dotnet new webapp -o ContactManager -au Individual -uld
    
  • Ajouter Models/Contact.cs:Add Models/Contact.cs:

    public class Contact
    {
        public int ContactId { get; set; }
        public string Name { get; set; }
        public string Address { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string Zip { get; set; }
        [DataType(DataType.EmailAddress)]
        public string Email { get; set; }
    }
    
  • Une structure le Contact modèle.Scaffold the Contact model.

  • La migration initiale de créer et mettre à jour de la base de données :Create initial migration and update the database:

dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
dotnet tool install -g dotnet-aspnet-codegenerator
dotnet aspnet-codegenerator razorpage -m Contact -udl -dc ApplicationDbContext -outDir Pages\Contacts --referenceScriptLibraries
dotnet ef database drop -f
dotnet ef migrations add initial
dotnet ef database update

Si vous rencontrez un bogue avec le dotnet aspnet-codegenerator razorpage de commande, consultez ce problème GitHub.If you experience a bug with the dotnet aspnet-codegenerator razorpage command, see this GitHub issue.

  • Mise à jour le ContactManager ancrer dans le Pages/Shared/_Layout.cshtml fichier :Update the ContactManager anchor in the Pages/Shared/_Layout.cshtml file:
<a class="navbar-brand" asp-area="" asp-page="/Contacts/Index">ContactManager</a>
  • Testez l’application en création, modification et suppression d’un contactTest the app by creating, editing, and deleting a contact

Amorcer la base de donnéesSeed the database

Ajouter le SeedData classe à la données dossier :Add the SeedData class to the Data folder:

using ContactManager.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
using System.Threading.Tasks;

// dotnet aspnet-codegenerator razorpage -m Contact -dc ApplicationDbContext -udl -outDir Pages\Contacts --referenceScriptLibraries

namespace ContactManager.Data
{
    public static class SeedData
    {
        public static async Task Initialize(IServiceProvider serviceProvider, string testUserPw)
        {
            using (var context = new ApplicationDbContext(
                serviceProvider.GetRequiredService<DbContextOptions<ApplicationDbContext>>()))
            {              
                SeedDB(context, "0");
            }
        }        

        public static void SeedDB(ApplicationDbContext context, string adminID)
        {
            if (context.Contact.Any())
            {
                return;   // DB has been seeded
            }

            context.Contact.AddRange(
                new Contact
                {
                    Name = "Debra Garcia",
                    Address = "1234 Main St",
                    City = "Redmond",
                    State = "WA",
                    Zip = "10999",
                    Email = "debra@example.com"
                },
                new Contact
                {
                    Name = "Thorsten Weinrich",
                    Address = "5678 1st Ave W",
                    City = "Redmond",
                    State = "WA",
                    Zip = "10999",
                    Email = "thorsten@example.com"
                },
             new Contact
             {
                 Name = "Yuhong Li",
                 Address = "9012 State st",
                 City = "Redmond",
                 State = "WA",
                 Zip = "10999",
                 Email = "yuhong@example.com"
             },
             new Contact
             {
                 Name = "Jon Orton",
                 Address = "3456 Maple St",
                 City = "Redmond",
                 State = "WA",
                 Zip = "10999",
                 Email = "jon@example.com"
             },
             new Contact
             {
                 Name = "Diliana Alexieva-Bosseva",
                 Address = "7890 2nd Ave E",
                 City = "Redmond",
                 State = "WA",
                 Zip = "10999",
                 Email = "diliana@example.com"
             }
             );
            context.SaveChanges();
        }

    }
}

Appelez SeedData.Initialize de Main:Call SeedData.Initialize from Main:

using ContactManager.Data;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;

namespace ContactManager
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var host = CreateHostBuilder(args).Build();

            using (var scope = host.Services.CreateScope())
            {
                var services = scope.ServiceProvider;

                try
                {
                    var context = services.GetRequiredService<ApplicationDbContext>();
                    context.Database.Migrate();
                    SeedData.Initialize(services, "not used");
                }
                catch (Exception ex)
                {
                    var logger = services.GetRequiredService<ILogger<Program>>();
                    logger.LogError(ex, "An error occurred seeding the DB.");
                }
            }

            host.Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}

Tester l’application d’amorçage la base de données.Test that the app seeded the database. S’il existe des lignes dans la base de données de contact, la méthode seed ne s’exécute pas.If there are any rows in the contact DB, the seed method doesn't run.

Ce didacticiel montre comment créer une application web ASP.NET Core avec des données utilisateur protégées par une autorisation.This tutorial shows how to create an ASP.NET Core web app with user data protected by authorization. Il affiche une liste de contacts (inscrits) les utilisateurs authentifiés ont créés.It displays a list of contacts that authenticated (registered) users have created. Il existe trois groupes de sécurité :There are three security groups:

  • Utilisateurs inscrits peut afficher toutes les données approuvées et peuvent modifier ou supprimer leurs propres données.Registered users can view all the approved data and can edit/delete their own data.
  • Gestionnaires de peuvent approuver ou rejeter des données de contact.Managers can approve or reject contact data. Seuls les contacts approuvés sont visibles aux utilisateurs.Only approved contacts are visible to users.
  • Les administrateurs peut approuver/rejeter et modifier ou de supprimer toutes les données.Administrators can approve/reject and edit/delete any data.

Dans l’image suivante, l’utilisateur Rick (rick@example.com) n’est connecté.In the following image, user Rick (rick@example.com) is signed in. Rick peut uniquement afficher les contacts approuvés et modifier/supprimer/créer un nouveau liens pour ses contacts.Rick can only view approved contacts and Edit/Delete/Create New links for his contacts. Seul le dernier enregistrement créé par Rick, affiche modifier et supprimer des liens.Only the last record, created by Rick, displays Edit and Delete links. Autres utilisateurs ne voient le dernier enregistrement jusqu'à ce qu’un gestionnaire ou un administrateur modifie le statut « Approved ».Other users won't see the last record until a manager or administrator changes the status to "Approved".

Capture d’écran montrant Rick connecté

Dans l’image suivante, manager@contoso.com est signé dans et dans le rôle :In the following image, manager@contoso.com is signed in and in the manager's role:

Capture d’écran manager@contoso.com connecté

L’illustration suivante montre les gestionnaires de vue des détails d’un contact :The following image shows the managers details view of a contact:

Vue du responsable d’un contact

Le approuver et rejeter boutons sont affichés uniquement pour les responsables et les administrateurs.The Approve and Reject buttons are only displayed for managers and administrators.

Dans l’image suivante, admin@contoso.com est signé dans et dans le rôle de l’administrateur :In the following image, admin@contoso.com is signed in and in the administrator's role:

Capture d’écran admin@contoso.com connecté

L’administrateur a tous les privilèges.The administrator has all privileges. Elle peut lire/modifier/supprimer un contact et modifier l’état de contacts.She can read/edit/delete any contact and change the status of contacts.

L’application a été créée par la structure suit Contact modèle :The app was created by scaffolding the following Contact model:

public class Contact
{
    public int ContactId { get; set; }
    public string Name { get; set; }
    public string Address { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Zip { get; set; }
    [DataType(DataType.EmailAddress)]
    public string Email { get; set; }
}

L’exemple contient les gestionnaires d’autorisation suivants :The sample contains the following authorization handlers:

  • ContactIsOwnerAuthorizationHandler: Garantit qu’un utilisateur peut modifier uniquement leurs données.ContactIsOwnerAuthorizationHandler: Ensures that a user can only edit their data.
  • ContactManagerAuthorizationHandler: Permet aux gestionnaires d’approuver ou rejeter des contacts.ContactManagerAuthorizationHandler: Allows managers to approve or reject contacts.
  • ContactAdministratorsAuthorizationHandler: Permet aux administrateurs d’approuver ou rejeter des contacts et à modifier/supprimer des contacts.ContactAdministratorsAuthorizationHandler: Allows administrators to approve or reject contacts and to edit/delete contacts.

PrérequisPrerequisites

Ce didacticiel est avancé.This tutorial is advanced. Vous devez être familiarisé avec :You should be familiar with:

Le démarrage et l’application terminéeThe starter and completed app

Télécharger le terminé application.Download the completed app. Test l’application terminée afin de vous familiariser avec ses fonctionnalités de sécurité.Test the completed app so you become familiar with its security features.

L’application de démarrageThe starter app

Télécharger le starter application.Download the starter app.

Exécutez l’application, appuyez sur la ContactManager lien et vérifiez que vous pouvez créer, modifier et supprimer un contact.Run the app, tap the ContactManager link, and verify you can create, edit, and delete a contact.

Sécuriser les données utilisateurSecure user data

Les sections suivantes ont toutes les principales étapes pour créer l’application de données utilisateur sécurisée.The following sections have all the major steps to create the secure user data app. Il peut s’avérer utile pour faire référence au projet terminé.You may find it helpful to refer to the completed project.

Lier les données de contact à l’utilisateurTie the contact data to the user

Utilisez ASP.NET identité ID d’utilisateur pour garantir les utilisateurs permettre modifier leurs données, mais pas d’autres données utilisateurs.Use the ASP.NET Identity user ID to ensure users can edit their data, but not other users data. Ajouter OwnerID et ContactStatus à la Contact modèle :Add OwnerID and ContactStatus to the Contact model:

public class Contact
{
    public int ContactId { get; set; }

    // user ID from AspNetUser table.
    public string OwnerID { get; set; }

    public string Name { get; set; }
    public string Address { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Zip { get; set; }
    [DataType(DataType.EmailAddress)]
    public string Email { get; set; }

    public ContactStatus Status { get; set; }
}

public enum ContactStatus
{
    Submitted,
    Approved,
    Rejected
}

OwnerID est l’ID d’utilisateur à partir de la AspNetUser table dans le identité base de données.OwnerID is the user's ID from the AspNetUser table in the Identity database. Le Status champ détermine si un contact est visible par les utilisateurs généraux.The Status field determines if a contact is viewable by general users.

Créer une nouvelle migration et mettre à jour de la base de données :Create a new migration and update the database:

dotnet ef migrations add userID_Status
dotnet ef database update

Ajouter des services de rôle à l’identitéAdd Role services to Identity

Ajouter fonctionnalité Ajouter des rôles pour ajouter des services de rôle :Append AddRoles to add Role services:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });

    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDefaultIdentity<IdentityUser>().AddRoles<IdentityRole>()
         .AddEntityFrameworkStores<ApplicationDbContext>();

Demander aux utilisateurs authentifiésRequire authenticated users

Définir la stratégie d’authentification par défaut pour exiger l’authentification des utilisateurs :Set the default authentication policy to require users to be authenticated:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });

    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDefaultIdentity<IdentityUser>().AddRoles<IdentityRole>()
         .AddEntityFrameworkStores<ApplicationDbContext>();

    services.AddMvc(config =>
    {
        // using Microsoft.AspNetCore.Mvc.Authorization;
        // using Microsoft.AspNetCore.Authorization;
        var policy = new AuthorizationPolicyBuilder()
                         .RequireAuthenticatedUser()
                         .Build();
        config.Filters.Add(new AuthorizeFilter(policy));
    })                
       .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

Vous pouvez refuser l’authentification au niveau de la méthode Page Razor, de contrôleur ou d’action avec la [AllowAnonymous] attribut.You can opt out of authentication at the Razor Page, controller, or action method level with the [AllowAnonymous] attribute. Définition de la stratégie d’authentification par défaut pour les utilisateurs doivent être authentifiés protège nouvellement ajouté les Pages Razor et les contrôleurs.Setting the default authentication policy to require users to be authenticated protects newly added Razor Pages and controllers. Avec l’authentification requise par défaut est plus sécurisée que de s’appuyer sur de nouveaux contrôleurs et les Pages Razor pour inclure le [Authorize] attribut.Having authentication required by default is more secure than relying on new controllers and Razor Pages to include the [Authorize] attribute.

Ajouter AllowAnonymous à l’Index, des pages sur et Contact pour les utilisateurs anonymes peuvent obtenir des informations sur le site avant ils s’inscrivent.Add AllowAnonymous to the Index, About, and Contact pages so anonymous users can get information about the site before they register.

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace ContactManager.Pages
{
    [AllowAnonymous]
    public class IndexModel : PageModel
    {
        public void OnGet()
        {

        }
    }
}

Configurer le compte de testConfigure the test account

Le SeedData classe crée deux comptes : administrateur et gestionnaire.The SeedData class creates two accounts: administrator and manager. Utilisez le outil Secret Manager pour définir un mot de passe pour ces comptes.Use the Secret Manager tool to set a password for these accounts. Définir le mot de passe à partir du répertoire de projet (le répertoire contenant Program.cs) :Set the password from the project directory (the directory containing Program.cs):

dotnet user-secrets set SeedUserPW <PW>

Si un mot de passe n’est pas spécifié, une exception est levée lorsque SeedData.Initialize est appelée.If a strong password is not specified, an exception is thrown when SeedData.Initialize is called.

Mise à jour Main à utiliser le mot de passe de test :Update Main to use the test password:

public class Program
{
    public static void Main(string[] args)
    {
        var host = CreateWebHostBuilder(args).Build();

        using (var scope = host.Services.CreateScope())
        {
            var services = scope.ServiceProvider;
            var context = services.GetRequiredService<ApplicationDbContext>();
            context.Database.Migrate();

            // requires using Microsoft.Extensions.Configuration;
            var config = host.Services.GetRequiredService<IConfiguration>();
            // Set password with the Secret Manager tool.
            // dotnet user-secrets set SeedUserPW <pw>

            var testUserPw = config["SeedUserPW"];
            try
            {
                SeedData.Initialize(services, testUserPw).Wait();
            }
            catch (Exception ex)
            {
                var logger = services.GetRequiredService<ILogger<Program>>();
                logger.LogError(ex.Message, "An error occurred seeding the DB.");
            }
        }

        host.Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>();
}

Créez les comptes de test et de mettre à jour les contactsCreate the test accounts and update the contacts

Mise à jour le Initialize méthode dans la SeedData classe pour créer les comptes de test :Update the Initialize method in the SeedData class to create the test accounts:

public static async Task Initialize(IServiceProvider serviceProvider, string testUserPw)
{
    using (var context = new ApplicationDbContext(
        serviceProvider.GetRequiredService<DbContextOptions<ApplicationDbContext>>()))
    {
        // For sample purposes seed both with the same password.
        // Password is set with the following:
        // dotnet user-secrets set SeedUserPW <pw>
        // The admin user can do anything

        var adminID = await EnsureUser(serviceProvider, testUserPw, "admin@contoso.com");
        await EnsureRole(serviceProvider, adminID, Constants.ContactAdministratorsRole);

        // allowed user can create and edit contacts that they create
        var managerID = await EnsureUser(serviceProvider, testUserPw, "manager@contoso.com");
        await EnsureRole(serviceProvider, managerID, Constants.ContactManagersRole);

        SeedDB(context, adminID);
    }
}

private static async Task<string> EnsureUser(IServiceProvider serviceProvider,
                                            string testUserPw, string UserName)
{
    var userManager = serviceProvider.GetService<UserManager<IdentityUser>>();

    var user = await userManager.FindByNameAsync(UserName);
    if (user == null)
    {
        user = new IdentityUser { UserName = UserName };
        await userManager.CreateAsync(user, testUserPw);
    }

    return user.Id;
}

private static async Task<IdentityResult> EnsureRole(IServiceProvider serviceProvider,
                                                              string uid, string role)
{
    IdentityResult IR = null;
    var roleManager = serviceProvider.GetService<RoleManager<IdentityRole>>();

    if (roleManager == null)
    {
        throw new Exception("roleManager null");
    }

    if (!await roleManager.RoleExistsAsync(role))
    {
        IR = await roleManager.CreateAsync(new IdentityRole(role));
    }

    var userManager = serviceProvider.GetService<UserManager<IdentityUser>>();

    var user = await userManager.FindByIdAsync(uid);

    if(user == null)
    {
        throw new Exception("The testUserPw password was probably not strong enough!");
    }
    
    IR = await userManager.AddToRoleAsync(user, role);

    return IR;
}

Ajouter l’ID d’utilisateur administrateur et ContactStatus aux contacts.Add the administrator user ID and ContactStatus to the contacts. Rendre des contacts « Envoyé » et un « rejeté ».Make one of the contacts "Submitted" and one "Rejected". Ajoutez l’ID d’utilisateur et l’état pour tous les contacts.Add the user ID and status to all the contacts. Un seul contact est affiché :Only one contact is shown:

public static void SeedDB(ApplicationDbContext context, string adminID)
{
    if (context.Contact.Any())
    {
        return;   // DB has been seeded
    }

    context.Contact.AddRange(
        new Contact
        {
            Name = "Debra Garcia",
            Address = "1234 Main St",
            City = "Redmond",
            State = "WA",
            Zip = "10999",
            Email = "debra@example.com",
            Status = ContactStatus.Approved,
            OwnerID = adminID
        },

Créer le propriétaire, le gestionnaire et gestionnaires d’autorisations d’administrateurCreate owner, manager, and administrator authorization handlers

Créer un ContactIsOwnerAuthorizationHandler classe dans le autorisation dossier.Create a ContactIsOwnerAuthorizationHandler class in the Authorization folder. Le ContactIsOwnerAuthorizationHandler vérifie que l’utilisateur agissant sur une ressource propriétaire de la ressource.The ContactIsOwnerAuthorizationHandler verifies that the user acting on a resource owns the resource.

using System.Threading.Tasks;
using ContactManager.Data;
using ContactManager.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Infrastructure;
using Microsoft.AspNetCore.Identity;

namespace ContactManager.Authorization
{
    public class ContactIsOwnerAuthorizationHandler
                : AuthorizationHandler<OperationAuthorizationRequirement, Contact>
    {
        UserManager<IdentityUser> _userManager;

        public ContactIsOwnerAuthorizationHandler(UserManager<IdentityUser> 
            userManager)
        {
            _userManager = userManager;
        }

        protected override Task
            HandleRequirementAsync(AuthorizationHandlerContext context,
                                   OperationAuthorizationRequirement requirement,
                                   Contact resource)
        {
            if (context.User == null || resource == null)
            {
                // Return Task.FromResult(0) if targeting a version of
                // .NET Framework older than 4.6:
                return Task.CompletedTask;
            }

            // If we're not asking for CRUD permission, return.

            if (requirement.Name != Constants.CreateOperationName &&
                requirement.Name != Constants.ReadOperationName   &&
                requirement.Name != Constants.UpdateOperationName &&
                requirement.Name != Constants.DeleteOperationName )
            {
                return Task.CompletedTask;
            }

            if (resource.OwnerID == _userManager.GetUserId(context.User))
            {
                context.Succeed(requirement);
            }

            return Task.CompletedTask;
        }
    }
}

Le ContactIsOwnerAuthorizationHandler appels contexte. Réussir si l’utilisateur authentifié actuel est le propriétaire du contact.The ContactIsOwnerAuthorizationHandler calls context.Succeed if the current authenticated user is the contact owner. Les gestionnaires d’autorisation généralement :Authorization handlers generally:

  • Retourner context.Succeed lorsque les conditions sont remplies.Return context.Succeed when the requirements are met.
  • Retourner Task.CompletedTask lorsque les conditions ne sont pas remplies.Return Task.CompletedTask when requirements aren't met. Task.CompletedTask n’est pas le succès ou l’échec—permet d’autres gestionnaires d’autorisation Exécuter.Task.CompletedTask is not success or failure—it allows other authorization handlers to run.

Si vous devez explicitement échouer, retourner contexte. Échec.If you need to explicitly fail, return context.Fail.

L’application permet aux propriétaires de contact de modifier, supprimer ou créer leurs propres données.The app allows contact owners to edit/delete/create their own data. ContactIsOwnerAuthorizationHandler n’a pas besoin vérifier l’opération passée dans le paramètre de condition.ContactIsOwnerAuthorizationHandler doesn't need to check the operation passed in the requirement parameter.

Créer un gestionnaire d’autorisation de gestionnaireCreate a manager authorization handler

Créer un ContactManagerAuthorizationHandler classe dans le autorisation dossier.Create a ContactManagerAuthorizationHandler class in the Authorization folder. Le ContactManagerAuthorizationHandler vérifie l’utilisateur agissant sur la ressource est un gestionnaire.The ContactManagerAuthorizationHandler verifies the user acting on the resource is a manager. Seuls les responsables peuvent approuver ou rejeter les modifications de contenu (nouvelle ou modifiées).Only managers can approve or reject content changes (new or changed).

using System.Threading.Tasks;
using ContactManager.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Infrastructure;
using Microsoft.AspNetCore.Identity;

namespace ContactManager.Authorization
{
    public class ContactManagerAuthorizationHandler :
        AuthorizationHandler<OperationAuthorizationRequirement, Contact>
    {
        protected override Task
            HandleRequirementAsync(AuthorizationHandlerContext context,
                                   OperationAuthorizationRequirement requirement,
                                   Contact resource)
        {
            if (context.User == null || resource == null)
            {
                return Task.CompletedTask;
            }

            // If not asking for approval/reject, return.
            if (requirement.Name != Constants.ApproveOperationName &&
                requirement.Name != Constants.RejectOperationName)
            {
                return Task.CompletedTask;
            }

            // Managers can approve or reject.
            if (context.User.IsInRole(Constants.ContactManagersRole))
            {
                context.Succeed(requirement);
            }

            return Task.CompletedTask;
        }
    }
}

Créer un gestionnaire d’autorisations d’administrateurCreate an administrator authorization handler

Créer un ContactAdministratorsAuthorizationHandler classe dans le autorisation dossier.Create a ContactAdministratorsAuthorizationHandler class in the Authorization folder. Le ContactAdministratorsAuthorizationHandler vérifie que l’utilisateur agissant sur la ressource est un administrateur.The ContactAdministratorsAuthorizationHandler verifies the user acting on the resource is an administrator. Administrateur peut effectuer toutes les opérations.Administrator can do all operations.

using System.Threading.Tasks;
using ContactManager.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Infrastructure;

namespace ContactManager.Authorization
{
    public class ContactAdministratorsAuthorizationHandler
                    : AuthorizationHandler<OperationAuthorizationRequirement, Contact>
    {
        protected override Task HandleRequirementAsync(
                                              AuthorizationHandlerContext context,
                                    OperationAuthorizationRequirement requirement, 
                                     Contact resource)
        {
            if (context.User == null)
            {
                return Task.CompletedTask;
            }

            // Administrators can do anything.
            if (context.User.IsInRole(Constants.ContactAdministratorsRole))
            {
                context.Succeed(requirement);
            }

            return Task.CompletedTask;
        }
    }
}

Inscrire les gestionnaires d’autorisationRegister the authorization handlers

Services à l’aide d’Entity Framework Core doivent être inscrit pour l’injection de dépendances à l’aide de AddScoped.Services using Entity Framework Core must be registered for dependency injection using AddScoped. Le ContactIsOwnerAuthorizationHandler utilise ASP.NET Core identité, qui est basé sur Entity Framework Core.The ContactIsOwnerAuthorizationHandler uses ASP.NET Core Identity, which is built on Entity Framework Core. Enregistrer les gestionnaires avec la collection de service afin qu’elles soient disponibles pour le ContactsController via l’injection de dépendances.Register the handlers with the service collection so they're available to the ContactsController through dependency injection. Ajoutez le code suivant à la fin de ConfigureServices:Add the following code to the end of ConfigureServices:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });

    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDefaultIdentity<IdentityUser>().AddRoles<IdentityRole>()
         .AddEntityFrameworkStores<ApplicationDbContext>();

    services.AddMvc(config =>
    {
        // using Microsoft.AspNetCore.Mvc.Authorization;
        // using Microsoft.AspNetCore.Authorization;
        var policy = new AuthorizationPolicyBuilder()
                         .RequireAuthenticatedUser()
                         .Build();
        config.Filters.Add(new AuthorizeFilter(policy));
    })                
       .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

    // Authorization handlers.
    services.AddScoped<IAuthorizationHandler,
                          ContactIsOwnerAuthorizationHandler>();

    services.AddSingleton<IAuthorizationHandler,
                          ContactAdministratorsAuthorizationHandler>();

    services.AddSingleton<IAuthorizationHandler,
                          ContactManagerAuthorizationHandler>();
}

ContactAdministratorsAuthorizationHandler et ContactManagerAuthorizationHandler sont ajoutés en tant que singletons.ContactAdministratorsAuthorizationHandler and ContactManagerAuthorizationHandler are added as singletons. Ils sont des singletons, car ils n’utilisent pas EF et toutes les informations nécessaires sont dans le Context paramètre de la HandleRequirementAsync (méthode).They're singletons because they don't use EF and all the information needed is in the Context parameter of the HandleRequirementAsync method.

Autorisation de la prise en chargeSupport authorization

Dans cette section, vous mettez à jour les Pages Razor et ajoutez une classe de configuration requise des opérations.In this section, you update the Razor Pages and add an operations requirements class.

Passez en revue la classe de configuration requise de contact des opérationsReview the contact operations requirements class

Examinez la ContactOperations classe.Review the ContactOperations class. Cette classe contient les exigences de l’application prend en charge :This class contains the requirements the app supports:

using Microsoft.AspNetCore.Authorization.Infrastructure;

namespace ContactManager.Authorization
{
    public static class ContactOperations
    {
        public static OperationAuthorizationRequirement Create =   
          new OperationAuthorizationRequirement {Name=Constants.CreateOperationName};
        public static OperationAuthorizationRequirement Read = 
          new OperationAuthorizationRequirement {Name=Constants.ReadOperationName};  
        public static OperationAuthorizationRequirement Update = 
          new OperationAuthorizationRequirement {Name=Constants.UpdateOperationName}; 
        public static OperationAuthorizationRequirement Delete = 
          new OperationAuthorizationRequirement {Name=Constants.DeleteOperationName};
        public static OperationAuthorizationRequirement Approve = 
          new OperationAuthorizationRequirement {Name=Constants.ApproveOperationName};
        public static OperationAuthorizationRequirement Reject = 
          new OperationAuthorizationRequirement {Name=Constants.RejectOperationName};
    }

    public class Constants
    {
        public static readonly string CreateOperationName = "Create";
        public static readonly string ReadOperationName = "Read";
        public static readonly string UpdateOperationName = "Update";
        public static readonly string DeleteOperationName = "Delete";
        public static readonly string ApproveOperationName = "Approve";
        public static readonly string RejectOperationName = "Reject";

        public static readonly string ContactAdministratorsRole = 
                                                              "ContactAdministrators";
        public static readonly string ContactManagersRole = "ContactManagers";
    }
}

Créer une classe de base pour les Pages Razor de ContactsCreate a base class for the Contacts Razor Pages

Créer une classe de base qui contient les services utilisés dans les Pages Razor de contacts.Create a base class that contains the services used in the contacts Razor Pages. La classe de base place le code d’initialisation dans un emplacement :The base class puts the initialization code in one location:

using ContactManager.Data;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace ContactManager.Pages.Contacts
{
    public class DI_BasePageModel : PageModel
    {
        protected ApplicationDbContext Context { get; }
        protected IAuthorizationService AuthorizationService { get; }
        protected UserManager<IdentityUser> UserManager { get; }

        public DI_BasePageModel(
            ApplicationDbContext context,
            IAuthorizationService authorizationService,
            UserManager<IdentityUser> userManager) : base()
        {
            Context = context;
            UserManager = userManager;
            AuthorizationService = authorizationService;
        } 
    }
}

Le code précédent :The preceding code:

  • Ajoute le IAuthorizationService service d’accéder aux gestionnaires d’autorisation.Adds the IAuthorizationService service to access to the authorization handlers.
  • Ajoute l’identité UserManager service.Adds the Identity UserManager service.
  • Ajoutez la ApplicationDbContext.Add the ApplicationDbContext.

Mettre à jour le CreateModelUpdate the CreateModel

Mettre à jour le constructeur de modèle de page de création à utiliser le DI_BasePageModel classe de base :Update the create page model constructor to use the DI_BasePageModel base class:

public class CreateModel : DI_BasePageModel
{
    public CreateModel(
        ApplicationDbContext context,
        IAuthorizationService authorizationService,
        UserManager<IdentityUser> userManager)
        : base(context, authorizationService, userManager)
    {
    }

Mise à jour le CreateModel.OnPostAsync méthode à :Update the CreateModel.OnPostAsync method to:

  • Ajoutez l’ID utilisateur pour la Contact modèle.Add the user ID to the Contact model.
  • Appeler le Gestionnaire d’autorisation pour vérifier que l’utilisateur est autorisé à créer des contacts.Call the authorization handler to verify the user has permission to create contacts.
public async Task<IActionResult> OnPostAsync()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    Contact.OwnerID = UserManager.GetUserId(User);

    // requires using ContactManager.Authorization;
    var isAuthorized = await AuthorizationService.AuthorizeAsync(
                                                User, Contact,
                                                ContactOperations.Create);
    if (!isAuthorized.Succeeded)
    {
        return new ChallengeResult();
    }

    Context.Contact.Add(Contact);
    await Context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Mettre à jour le IndexModelUpdate the IndexModel

Mise à jour le OnGetAsync méthode approuvées uniquement les contacts sont présentés aux utilisateurs généraux :Update the OnGetAsync method so only approved contacts are shown to general users:

public class IndexModel : DI_BasePageModel
{
    public IndexModel(
        ApplicationDbContext context,
        IAuthorizationService authorizationService,
        UserManager<IdentityUser> userManager)
        : base(context, authorizationService, userManager)
    {
    }

    public IList<Contact> Contact { get; set; }

    public async Task OnGetAsync()
    {
        var contacts = from c in Context.Contact
                       select c;

        var isAuthorized = User.IsInRole(Constants.ContactManagersRole) ||
                           User.IsInRole(Constants.ContactAdministratorsRole);

        var currentUserId = UserManager.GetUserId(User);

        // Only approved contacts are shown UNLESS you're authorized to see them
        // or you are the owner.
        if (!isAuthorized)
        {
            contacts = contacts.Where(c => c.Status == ContactStatus.Approved
                                        || c.OwnerID == currentUserId);
        }

        Contact = await contacts.ToListAsync();
    }
}

Mettre à jour le EditModelUpdate the EditModel

Ajoutez un gestionnaire d’autorisation pour vérifier que l’utilisateur propriétaire du contact.Add an authorization handler to verify the user owns the contact. Étant donné que l’autorisation de ressource est en cours de validation, le [Authorize] attribut n’est pas suffisant.Because resource authorization is being validated, the [Authorize] attribute is not enough. L’application n’a pas accès à la ressource lors de l’évaluation des attributs.The app doesn't have access to the resource when attributes are evaluated. Autorisation basée sur la ressource doit être impérative.Resource-based authorization must be imperative. Vérifications doivent être effectuées une fois que l’application a accès à la ressource, en le chargeant dans le modèle de page ou en le chargeant dans le gestionnaire lui-même.Checks must be performed once the app has access to the resource, either by loading it in the page model or by loading it within the handler itself. Vous accédez fréquemment la ressource en passant la clé de ressource.You frequently access the resource by passing in the resource key.

public class EditModel : DI_BasePageModel
{
    public EditModel(
        ApplicationDbContext context,
        IAuthorizationService authorizationService,
        UserManager<IdentityUser> userManager)
        : base(context, authorizationService, userManager)
    {
    }

    [BindProperty]
    public Contact Contact { get; set; }

    public async Task<IActionResult> OnGetAsync(int id)
    {
        Contact = await Context.Contact.FirstOrDefaultAsync(
                                             m => m.ContactId == id);

        if (Contact == null)
        {
            return NotFound();
        }

        var isAuthorized = await AuthorizationService.AuthorizeAsync(
                                                  User, Contact,
                                                  ContactOperations.Update);
        if (!isAuthorized.Succeeded)
        {
            return new ChallengeResult();
        }

        return Page();
    }

    public async Task<IActionResult> OnPostAsync(int id)
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        // Fetch Contact from DB to get OwnerID.
        var contact = await Context
            .Contact.AsNoTracking()
            .FirstOrDefaultAsync(m => m.ContactId == id);

        if (contact == null)
        {
            return NotFound();
        }

        var isAuthorized = await AuthorizationService.AuthorizeAsync(
                                                 User, contact,
                                                 ContactOperations.Update);
        if (!isAuthorized.Succeeded)
        {
            return new ChallengeResult();
        }

        Contact.OwnerID = contact.OwnerID;

        Context.Attach(Contact).State = EntityState.Modified;

        if (contact.Status == ContactStatus.Approved)
        {
            // If the contact is updated after approval, 
            // and the user cannot approve,
            // set the status back to submitted so the update can be
            // checked and approved.
            var canApprove = await AuthorizationService.AuthorizeAsync(User,
                                    contact,
                                    ContactOperations.Approve);

            if (!canApprove.Succeeded)
            {
                contact.Status = ContactStatus.Submitted;
            }
        }

        await Context.SaveChangesAsync();

        return RedirectToPage("./Index");
    }

    private bool ContactExists(int id)
    {
        return Context.Contact.Any(e => e.ContactId == id);
    }
}

Mettre à jour le DeleteModelUpdate the DeleteModel

Mettre à jour le modèle de page delete pour utiliser le Gestionnaire d’autorisation pour vérifier que l’utilisateur a l’autorisation de suppression sur le contact.Update the delete page model to use the authorization handler to verify the user has delete permission on the contact.

public class DeleteModel : DI_BasePageModel
{
    public DeleteModel(
        ApplicationDbContext context,
        IAuthorizationService authorizationService,
        UserManager<IdentityUser> userManager)
        : base(context, authorizationService, userManager)
    {
    }

    [BindProperty]
    public Contact Contact { get; set; }

    public async Task<IActionResult> OnGetAsync(int id)
    {
        Contact = await Context.Contact.FirstOrDefaultAsync(
                                             m => m.ContactId == id);

        if (Contact == null)
        {
            return NotFound();
        }

        var isAuthorized = await AuthorizationService.AuthorizeAsync(
                                                 User, Contact,
                                                 ContactOperations.Delete);
        if (!isAuthorized.Succeeded)
        {
            return new ChallengeResult();
        }

        return Page();
    }

    public async Task<IActionResult> OnPostAsync(int id)
    {
        Contact = await Context.Contact.FindAsync(id);

        var contact = await Context
            .Contact.AsNoTracking()
            .FirstOrDefaultAsync(m => m.ContactId == id);

        if (contact == null)
        {
            return NotFound();
        }

        var isAuthorized = await AuthorizationService.AuthorizeAsync(
                                                 User, contact,
                                                 ContactOperations.Delete);
        if (!isAuthorized.Succeeded)
        {
            return new ChallengeResult();
        }

        Context.Contact.Remove(Contact);
        await Context.SaveChangesAsync();

        return RedirectToPage("./Index");
    }
}

Injecter le service d’autorisation dans les vuesInject the authorization service into the views

Actuellement, le montre l’interface utilisateur modifie et supprime des liens pour les contacts de que l’utilisateur ne peut pas modifier.Currently, the UI shows edit and delete links for contacts the user can't modify.

Injecter le service d’autorisation dans le Views/_viewimports.cshtml afin qu’il soit disponible pour toutes les vues de fichiers :Inject the authorization service in the Views/_ViewImports.cshtml file so it's available to all views:

@using Microsoft.AspNetCore.Identity
@using ContactManager
@using ContactManager.Data
@namespace ContactManager.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@using ContactManager.Authorization;
@using Microsoft.AspNetCore.Authorization
@using ContactManager.Models
@inject IAuthorizationService AuthorizationService

Le balisage précédent ajoute plusieurs using instructions.The preceding markup adds several using statements.

Mise à jour le modifier et supprimer lie dans Pages/Contacts/Index.cshtml afin d’être affichées uniquement pour les utilisateurs disposant des autorisations appropriées :Update the Edit and Delete links in Pages/Contacts/Index.cshtml so they're only rendered for users with the appropriate permissions:

@page
@model ContactManager.Pages.Contacts.IndexModel

@{
    ViewData["Title"] = "Index";
}

<h2>Index</h2>

<p>
    <a asp-page="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Contact[0].Name)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Contact[0].Address)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Contact[0].City)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Contact[0].State)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Contact[0].Zip)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Contact[0].Email)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Contact[0].Status)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model.Contact)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.Name)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Address)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.City)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.State)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Zip)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Email)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Status)
                </td>
                <td>
                    @if ((await AuthorizationService.AuthorizeAsync(
                     User, item,
                     ContactOperations.Update)).Succeeded)
                    {
                        <a asp-page="./Edit" asp-route-id="@item.ContactId">Edit</a>
                        <text> | </text>
                    }

                    <a asp-page="./Details" asp-route-id="@item.ContactId">Details</a>

                    @if ((await AuthorizationService.AuthorizeAsync(
                     User, item,
                     ContactOperations.Delete)).Succeeded)
                    {
                        <text> | </text>
                        <a asp-page="./Delete" asp-route-id="@item.ContactId">Delete</a>
                    }
                </td>
            </tr>
        }
    </tbody>
</table>

Avertissement

Masquage des liens à partir des utilisateurs qui ne sont pas autorisés à modifier les données ne sécuriser l’application.Hiding links from users that don't have permission to change data doesn't secure the app. Masquage des liens rend l’application plus conviviale en affichant les liens ne sont valides.Hiding links makes the app more user-friendly by displaying only valid links. Les utilisateurs peuvent hack l’URL générées pour appeler modifier et supprimer des opérations sur les données qu’ils ne possèdent pas.Users can hack the generated URLs to invoke edit and delete operations on data they don't own. Le contrôleur ou une Page Razor doit appliquer les vérifications d’accès pour sécuriser les données.The Razor Page or controller must enforce access checks to secure the data.

Détails de la mise à jourUpdate Details

Mettre à jour l’affichage des détails pour les responsables peuvent approuver ou rejeter des contacts :Update the details view so managers can approve or reject contacts:

        @*Precedng markup omitted for brevity.*@
        <dt>
            @Html.DisplayNameFor(model => model.Contact.Email)
        </dt>
        <dd>
            @Html.DisplayFor(model => model.Contact.Email)
        </dd>
        <dt>
            @Html.DisplayNameFor(model => model.Contact.Status)
        </dt>
        <dd>
            @Html.DisplayFor(model => model.Contact.Status)
        </dd>
    </dl>
</div>

@if (Model.Contact.Status != ContactStatus.Approved)
{
    @if ((await AuthorizationService.AuthorizeAsync(
     User, Model.Contact, ContactOperations.Approve)).Succeeded)
    {
        <form style="display:inline;" method="post">
            <input type="hidden" name="id" value="@Model.Contact.ContactId" />
            <input type="hidden" name="status" value="@ContactStatus.Approved" />
            <button type="submit" class="btn btn-xs btn-success">Approve</button>
        </form>
    }
}

@if (Model.Contact.Status != ContactStatus.Rejected)
{
    @if ((await AuthorizationService.AuthorizeAsync(
     User, Model.Contact, ContactOperations.Reject)).Succeeded)
    {
        <form style="display:inline;" method="post">
            <input type="hidden" name="id" value="@Model.Contact.ContactId" />
            <input type="hidden" name="status" value="@ContactStatus.Rejected" />
            <button type="submit" class="btn btn-xs btn-success">Reject</button>
        </form>
    }
}

<div>
    @if ((await AuthorizationService.AuthorizeAsync(
         User, Model.Contact,
         ContactOperations.Update)).Succeeded)
    {
        <a asp-page="./Edit" asp-route-id="@Model.Contact.ContactId">Edit</a>
        <text> | </text>
    }
    <a asp-page="./Index">Back to List</a>
</div>

Mettre à jour le modèle de page de détails :Update the details page model:

public class DetailsModel : DI_BasePageModel
{
    public DetailsModel(
        ApplicationDbContext context,
        IAuthorizationService authorizationService,
        UserManager<IdentityUser> userManager)
        : base(context, authorizationService, userManager)
    {
    }

    public Contact Contact { get; set; }

    public async Task<IActionResult> OnGetAsync(int id)
    {
        Contact = await Context.Contact.FirstOrDefaultAsync(m => m.ContactId == id);

        if (Contact == null)
        {
            return NotFound();
        }

        var isAuthorized = User.IsInRole(Constants.ContactManagersRole) ||
                           User.IsInRole(Constants.ContactAdministratorsRole);

        var currentUserId = UserManager.GetUserId(User);

        if (!isAuthorized 
            &&  currentUserId != Contact.OwnerID
            && Contact.Status != ContactStatus.Approved) 
        {
            return new ChallengeResult();
        }

        return Page();
    }

    public async Task<IActionResult> OnPostAsync(int id, ContactStatus status)
    {
        var contact = await Context.Contact.FirstOrDefaultAsync(
                                                  m => m.ContactId == id);

        if (contact == null)
        {
            return NotFound();
        }

        var contactOperation = (status == ContactStatus.Approved)
                                                   ? ContactOperations.Approve
                                                   : ContactOperations.Reject;

        var isAuthorized = await AuthorizationService.AuthorizeAsync(User, contact,
                                    contactOperation);
        if (!isAuthorized.Succeeded)
        {
            return new ChallengeResult();
        }
        contact.Status = status;
        Context.Contact.Update(contact);
        await Context.SaveChangesAsync();

        return RedirectToPage("./Index");
    }
}

Ajouter ou supprimer un utilisateur à un rôleAdd or remove a user to a role

Consultez ce problème pour plus d’informations sur :See this issue for information on:

  • Suppression de privilèges à partir d’un utilisateur.Removing privileges from a user. Désactivation par exemple, un utilisateur dans une application de conversation.For example, muting a user in a chat app.
  • Ajout des privilèges à un utilisateur.Adding privileges to a user.

Tester l’application terminéeTest the completed app

Si vous n’avez pas déjà défini un mot de passe pour les comptes d’utilisateur amorcée, utilisez le outil Secret Manager pour définir un mot de passe :If you haven't already set a password for seeded user accounts, use the Secret Manager tool to set a password:

  • Choisissez un mot de passe fort : Utiliser huit ou plus de caractères et au moins un caractère majuscule, numéro et de symboles.Choose a strong password: Use eight or more characters and at least one upper-case character, number, and symbol. Par exemple, Passw0rd! répond aux exigences de mot de passe fort.For example, Passw0rd! meets the strong password requirements.

  • Exécutez la commande suivante à partir du dossier du projet, où <PW> est le mot de passe :Execute the following command from the project's folder, where <PW> is the password:

    dotnet user-secrets set SeedUserPW <PW>
    
  • Supprimer et mettre à jour la base de donnéesDrop and update the database

     dotnet ef database drop -f
     dotnet ef database update  
    

* <span data-ttu-id="fcc64-442">Redémarrez l’application pour amorcer la base de données.</span><span class="sxs-lookup"><span data-stu-id="fcc64-442">Restart the app to seed the database.</span></span>

<span data-ttu-id="fcc64-443">Un moyen simple de tester l’application terminée consiste à lancer les trois différents navigateurs (ou incognito/InPrivate sessions).</span><span class="sxs-lookup"><span data-stu-id="fcc64-443">An easy way to test the completed app is to launch three different browsers (or incognito/InPrivate sessions).</span></span> <span data-ttu-id="fcc64-444">Dans un navigateur, inscrire un nouvel utilisateur (par exemple, `test@example.com`).</span><span class="sxs-lookup"><span data-stu-id="fcc64-444">In one browser, register a new user (for example, `test@example.com`).</span></span> <span data-ttu-id="fcc64-445">Connectez-vous à chaque navigateur avec un autre utilisateur.</span><span class="sxs-lookup"><span data-stu-id="fcc64-445">Sign in to each browser with a different user.</span></span> <span data-ttu-id="fcc64-446">Vérifiez les opérations suivantes :</span><span class="sxs-lookup"><span data-stu-id="fcc64-446">Verify the following operations:</span></span>

* <span data-ttu-id="fcc64-447">Les utilisateurs inscrits peuvent afficher toutes les données de contact approuvées.</span><span class="sxs-lookup"><span data-stu-id="fcc64-447">Registered users can view all of the approved contact data.</span></span>
* <span data-ttu-id="fcc64-448">Les utilisateurs inscrits peuvent modifier ou supprimer leurs propres données.</span><span class="sxs-lookup"><span data-stu-id="fcc64-448">Registered users can edit/delete their own data.</span></span>
* <span data-ttu-id="fcc64-449">Gestionnaires peuvent approuver/rejeter les données de contact.</span><span class="sxs-lookup"><span data-stu-id="fcc64-449">Managers can approve/reject contact data.</span></span> <span data-ttu-id="fcc64-450">Le `Details` afficher montre **approuver** et **rejeter** boutons.</span><span class="sxs-lookup"><span data-stu-id="fcc64-450">The `Details` view shows **Approve** and **Reject** buttons.</span></span>
* <span data-ttu-id="fcc64-451">Les administrateurs peuvent approuver/rejeter et modifier ou de supprimer toutes les données.</span><span class="sxs-lookup"><span data-stu-id="fcc64-451">Administrators can approve/reject and edit/delete all data.</span></span>

| <span data-ttu-id="fcc64-452">Utilisateur</span><span class="sxs-lookup"><span data-stu-id="fcc64-452">User</span></span>                | <span data-ttu-id="fcc64-453">Amorcée par l’application</span><span class="sxs-lookup"><span data-stu-id="fcc64-453">Seeded by the app</span></span> | <span data-ttu-id="fcc64-454">Options</span><span class="sxs-lookup"><span data-stu-id="fcc64-454">Options</span></span>                                  |
| ------------------- | :---------------: | ---------------------------------------- |
| test@example.com    | <span data-ttu-id="fcc64-455">Non</span><span class="sxs-lookup"><span data-stu-id="fcc64-455">No</span></span>                | <span data-ttu-id="fcc64-456">Modifier/supprimer les données propres.</span><span class="sxs-lookup"><span data-stu-id="fcc64-456">Edit/delete the own data.</span></span>                |
| manager@contoso.com | <span data-ttu-id="fcc64-457">Oui</span><span class="sxs-lookup"><span data-stu-id="fcc64-457">Yes</span></span>               | <span data-ttu-id="fcc64-458">Approuver/rejeter et modifier ou de supprimer des données propres.</span><span class="sxs-lookup"><span data-stu-id="fcc64-458">Approve/reject and edit/delete own data.</span></span> |
| admin@contoso.com   | <span data-ttu-id="fcc64-459">Oui</span><span class="sxs-lookup"><span data-stu-id="fcc64-459">Yes</span></span>               | <span data-ttu-id="fcc64-460">Approuver/rejeter et de modifier ou de supprimer toutes les données.</span><span class="sxs-lookup"><span data-stu-id="fcc64-460">Approve/reject and edit/delete all data.</span></span> |

<span data-ttu-id="fcc64-461">Créer un contact dans le navigateur de l’administrateur.</span><span class="sxs-lookup"><span data-stu-id="fcc64-461">Create a contact in the administrator's browser.</span></span> <span data-ttu-id="fcc64-462">Copiez l’URL pour supprimer et modifier à partir du contact de l’administrateur.</span><span class="sxs-lookup"><span data-stu-id="fcc64-462">Copy the URL for delete and edit from the administrator contact.</span></span> <span data-ttu-id="fcc64-463">Collez ces liens dans le navigateur de l’utilisateur de test pour vérifier que l’utilisateur de test ne peut pas effectuer ces opérations.</span><span class="sxs-lookup"><span data-stu-id="fcc64-463">Paste these links into the test user's browser to verify the test user can't perform these operations.</span></span>

## <a name="create-the-starter-app"></a><span data-ttu-id="fcc64-464">Créer l’application de démarrage</span><span class="sxs-lookup"><span data-stu-id="fcc64-464">Create the starter app</span></span>

* <span data-ttu-id="fcc64-465">Créer une application Pages Razor nommée « ContactManager »</span><span class="sxs-lookup"><span data-stu-id="fcc64-465">Create a Razor Pages app named "ContactManager"</span></span>
  * <span data-ttu-id="fcc64-466">Créer l’application avec **comptes d’utilisateur individuels**.</span><span class="sxs-lookup"><span data-stu-id="fcc64-466">Create the app with **Individual User Accounts**.</span></span>
  * <span data-ttu-id="fcc64-467">Nommez-Le « ContactManager » afin de l’espace de noms correspond à l’espace de noms utilisé dans l’exemple.</span><span class="sxs-lookup"><span data-stu-id="fcc64-467">Name it "ContactManager" so the namespace matches the namespace used in the sample.</span></span>
  * <span data-ttu-id="fcc64-468">`-uld` Spécifie la base de données locale au lieu de SQLite</span><span class="sxs-lookup"><span data-stu-id="fcc64-468">`-uld` specifies LocalDB instead of SQLite</span></span>

  ```console
  dotnet new webapp -o ContactManager -au Individual -uld
  • Ajouter Models/Contact.cs:Add Models/Contact.cs:

    public class Contact
    {
        public int ContactId { get; set; }
        public string Name { get; set; }
        public string Address { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string Zip { get; set; }
        [DataType(DataType.EmailAddress)]
        public string Email { get; set; }
    }
    
  • Une structure le Contact modèle.Scaffold the Contact model.

  • La migration initiale de créer et mettre à jour de la base de données :Create initial migration and update the database:

    dotnet aspnet-codegenerator razorpage -m Contact -udl -dc ApplicationDbContext -outDir Pages\Contacts --referenceScriptLibraries
    dotnet ef database drop -f
    dotnet ef migrations add initial
    dotnet ef database update
    
  • Mise à jour le ContactManager ancrer dans le pages/_Layout.cshtml fichier :Update the ContactManager anchor in the Pages/_Layout.cshtml file:

    <a asp-page="/Contacts/Index" class="navbar-brand">ContactManager</a>
    
  • Testez l’application en création, modification et suppression d’un contactTest the app by creating, editing, and deleting a contact

Amorcer la base de donnéesSeed the database

Ajouter le SeedData classe à la données dossier.Add the SeedData class to the Data folder.

Appelez SeedData.Initialize de Main:Call SeedData.Initialize from Main:

public class Program
{
    public static void Main(string[] args)
    {
        var host = CreateWebHostBuilder(args).Build();

        using (var scope = host.Services.CreateScope())
        {
            var services = scope.ServiceProvider;

            try
            {
                var context = services.GetRequiredService<ApplicationDbContext>();
                context.Database.Migrate();
                SeedData.Initialize(services, "not used");
            }
            catch (Exception ex)
            {
                var logger = services.GetRequiredService<ILogger<Program>>();
                logger.LogError(ex, "An error occurred seeding the DB.");
            }
        }

        host.Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>();
}

Tester l’application d’amorçage la base de données.Test that the app seeded the database. S’il existe des lignes dans la base de données de contact, la méthode seed ne s’exécute pas.If there are any rows in the contact DB, the seed method doesn't run.

Ressources supplémentairesAdditional resources