Megosztás a következőn keresztül:


ASP.NET Core-webalkalmazás létrehozása engedélyezéssel védett felhasználói adatokkal

Készítette: Rick Anderson és Joe Audette

Ez az oktatóanyag bemutatja, hogyan hozhat létre ASP.NET Core-webalkalmazást az engedélyezés által védett felhasználói adatokkal. Megjeleníti a hitelesített (regisztrált) felhasználók által létrehozott névjegyek listáját. Három biztonsági csoport létezik:

  • A regisztrált felhasználók megtekinthetik az összes jóváhagyott adatot, és szerkeszthetik/törölhetik saját adataikat.
  • A vezetők jóváhagyhatják vagy elutasíthatják a kapcsolattartási adatokat. Csak a jóváhagyott névjegyek láthatók a felhasználók számára.
  • A rendszergazdák bármilyen adatot jóváhagyhatnak/elutasíthatnak, illetve szerkeszthetik/törölhetnek.

A dokumentum képei nem felelnek meg pontosan a legújabb sablonoknak.

Az alábbi képen Rick (rick@example.com) felhasználó van bejelentkezve. Rick csak a jóváhagyott névjegyeket tekintheti meg, és a névjegyeihez szerkesztheti/törölheti/újakat hozhat létre. Csak a Rick által létrehozott utolsó rekord jeleníti meg a hivatkozások szerkesztését és törlését . A többi felhasználó nem fogja látni az utolsó rekordot, amíg egy felettes vagy rendszergazda nem módosítja a "Jóváhagyott" állapotot.

Képernyőkép Rick bejelentkezésről

Az alábbi képen manager@contoso.com be van jelentkezve és a menedzser szerepkörében van:

Képernyőkép a bejelentkezésről manager@contoso.com

Az alábbi képen egy kapcsolat kezelőjének részletes nézete látható.

Egy kapcsolattartó menedzser nézete

A Jóváhagyás és elutasítás gomb csak a vezetők és rendszergazdák számára jelenik meg.

Az alábbi képen a admin@contoso.com be van jelentkezve, és az adminisztrátori szerepkörben van.

Képernyőkép a bejelentkezésről admin@contoso.com

A rendszergazda minden jogosultsággal rendelkezik. Bármely kapcsolatot elolvashat, szerkeszthet vagy törölhet, és módosíthatja a kapcsolatok állapotát.

Az alkalmazás a következő modell Contact:

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

A minta a következő engedélyezési kezelőket tartalmazza:

  • ContactIsOwnerAuthorizationHandler: Biztosítja, hogy a felhasználók csak az adataikat szerkeszthessék.
  • ContactManagerAuthorizationHandler: Lehetővé teszi a vezetők számára a partnerek jóváhagyását vagy elutasítását.
  • ContactAdministratorsAuthorizationHandler: Lehetővé teszi a rendszergazdák számára a névjegyek jóváhagyását vagy elutasítását, valamint a névjegyek szerkesztését/törlését.

Prerequisites

Ez az oktatóanyag speciális. Ismernie kell a következőt:

A kezdő és befejezett alkalmazás

Töltse le a kész alkalmazást. Tesztelje a kész alkalmazást, hogy megismerje annak biztonsági funkcióit.

Tip

A git sparse-checkout használatával csak a minta almappáját lehet letölteni. Például:

git clone --depth 1 --filter=blob:none https://github.com/dotnet/AspNetCore.Docs.git --sparse
cd AspNetCore.Docs
git sparse-checkout init --cone
git sparse-checkout set aspnetcore/security/authorization/secure-data/samples

A kezdőalkalmazás

Töltse le a kezdőalkalmazást .

Futtassa az alkalmazást, koppintson a ContactManager hivatkozásra, és ellenőrizze, hogy létrehozhat, szerkeszthet és törölhet-e partnereket. A kezdőalkalmazás létrehozásához lásd : A kezdőalkalmazás létrehozása.

Felhasználói adatok védelme

Az alábbi szakaszok a biztonságos felhasználói adatalkalmazás létrehozásának összes fő lépését ismertetik. Hasznos lehet, ha a befejezett projektre hivatkozik.

Kapcsolattartási adatok kötése a felhasználóhoz

A ASP.NET Identity felhasználói azonosítóval biztosíthatja, hogy a felhasználók szerkeszthessék az adataikat, más felhasználói adatokat azonban nem. Hozzáadás OwnerID és ContactStatus a Contact modellhez:

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 a felhasználó azonosítója az AspNetUser adatbázis táblájából Identity . A Status mező meghatározza, hogy a kapcsolatokat megtekinthetik-e általában a felhasználók.

Hozzon létre egy új migrálást, és frissítse az adatbázist:

dotnet ef migrations add userID_Status
dotnet ef database update

Szerepkör-szolgáltatások hozzáadása Identity

AddRoles hozzáfűzése a szerepkör-szolgáltatások hozzáadásához:

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(
    options => options.SignIn.RequireConfirmedAccount = true)
    .AddRoles<IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>();

Hitelesített felhasználók megkövetelése

Állítsa be a tartalék engedélyezési szabályzatot a felhasználók hitelesítésének megkövetelésére:

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(
    options => options.SignIn.RequireConfirmedAccount = true)
    .AddRoles<IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>();

builder.Services.AddRazorPages();

builder.Services.AddAuthorization(options =>
{
    options.FallbackPolicy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .Build();
});

Az előző kiemelt kód beállítja a tartalék engedélyezési szabályzatot. A tartalék engedélyezési szabályzathoz minden felhasználót hitelesíteni kell, kivéve Razor az oldalakat, a vezérlőket vagy az engedélyezési attribútummal rendelkező műveleti módszereket. Például az Razor lapok, a [AllowAnonymous] vagy [Authorize(PolicyName="MyPolicy")] vezérlők, illetve műveleti módszerek az alkalmazott engedélyezési attribútumot használják a tartalék engedélyezési szabályzat helyett.

RequireAuthenticatedUser hozzáadja a DenyAnonymousAuthorizationRequirement-et az aktuális példányhoz, amely biztosítja, hogy az aktuális felhasználó hitelesített legyen.

A tartalék engedélyezési szabályzat:

  • A rendszer minden olyan kérelemre alkalmazza, amely nem határoz meg explicit módon engedélyezési szabályzatot. A végpont-útválasztás által kiszolgált kérések esetében ez minden olyan végpontot is magában foglal, amely nem ad meg engedélyezési attribútumot. Az engedélyezési köztes szoftver ( például statikus fájlok) után más köztes szoftver által kiszolgált kérések esetében ez minden kérésre alkalmazza a szabályzatot.

Ha a tartalék engedélyezési szabályzatot úgy állítja be, hogy a felhasználókat hitelesíteni kell, az védi az újonnan hozzáadott Razor oldalakat és vezérlőket. Az alapértelmezés szerint szükséges engedélyezés biztonságosabb, mint az új vezérlőkre Razor és a Pagesre támaszkodni az [Authorize] attribútum belefoglalásához.

Az AuthorizationOptions osztály AuthorizationOptions.DefaultPolicy-t is tartalmaz. Az DefaultPolicy az a szabályzat, amelyet a [Authorize] attribútummal használnak, ha nincs másik megadva. [Authorize] nem tartalmaz elnevezett szabályzatot, ellentétben a [Authorize(PolicyName="MyPolicy")].

A szabályzatokkal kapcsolatos további információkért lásd a szabályzatalapú engedélyezést a ASP.NET Core-ban.

Az MVC-vezérlők és Razor Pages esetében az összes felhasználó hitelesítése megkövetelésének alternatív módja egy engedélyezési szűrő hozzáadása.

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

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(
    options => options.SignIn.RequireConfirmedAccount = true)
    .AddRoles<IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>();

builder.Services.AddRazorPages();

builder.Services.AddControllers(config =>
{
    var policy = new AuthorizationPolicyBuilder()
                     .RequireAuthenticatedUser()
                     .Build();
    config.Filters.Add(new AuthorizeFilter(policy));
});

var app = builder.Build();

Az előző kód egy engedélyezési szűrőt használ, a tartalék szabályzat beállítása végpont-útválasztást használ. A tartalék házirend beállítása az elsődleges módszer az összes felhasználó hitelesítésének megkövetelésére.

Adja hozzá az AllowAnonymous-t a IndexPrivacy lapokhoz, hogy a névtelen felhasználók a regisztráció előtt információt szerezzenek a webhelyről:

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

namespace ContactManager.Pages;

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

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

    public void OnGet()
    {

    }
}

A tesztfiók konfigurálása

Az SeedData osztály két fiókot hoz létre: rendszergazda és felettes. A Titkos kódkezelő eszközzel jelszót állíthat be ezekhez a fiókokhoz. Állítsa be a jelszót a projektnévtárban (a Program.cs könyvtár):

dotnet user-secrets set SeedUserPW <PW>

Ha gyenge jelszót ad meg, a rendszer kivételt jelez a meghíváskor SeedData.Initialize .

Frissítse az alkalmazást a tesztjelszó használatára:

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(
    options => options.SignIn.RequireConfirmedAccount = true)
    .AddRoles<IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>();

builder.Services.AddRazorPages();

builder.Services.AddAuthorization(options =>
{
    options.FallbackPolicy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .Build();
});

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

builder.Services.AddSingleton<IAuthorizationHandler,
                      ContactAdministratorsAuthorizationHandler>();

builder.Services.AddSingleton<IAuthorizationHandler,
                      ContactManagerAuthorizationHandler>();

var app = builder.Build();

using (var scope = app.Services.CreateScope())
{
    var services = scope.ServiceProvider;
    var context = services.GetRequiredService<ApplicationDbContext>();
    context.Database.Migrate();
    // requires using Microsoft.Extensions.Configuration;
    // Set password with the Secret Manager tool.
    // dotnet user-secrets set SeedUserPW <pw>

    var testUserPw = builder.Configuration.GetValue<string>("SeedUserPW");

   await SeedData.Initialize(services, testUserPw);
}

Hozza létre a tesztfiókokat és frissítse a névjegyeket

Frissítse az Initialize osztály metódusát SeedData a tesztfiókok létrehozásához:

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)
{
    var roleManager = serviceProvider.GetService<RoleManager<IdentityRole>>();

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

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

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

    //if (userManager == null)
    //{
    //    throw new Exception("userManager is null");
    //}

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

Adja hozzá a rendszergazdai felhasználói azonosítót és ContactStatus a névjegyekhez. Az egyik névjegyet "Elküldöttként", a másikat pedig "Elutasítottként" állítsa be. Adja hozzá a felhasználói azonosítót és az állapotot az összes kapcsolathoz. Csak egy kapcsolat jelenik meg:

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

Tulajdonosi, kezelői és rendszergazdai engedélyezési kezelők létrehozása

Hozzon létre egy osztályt ContactIsOwnerAuthorizationHandler az Engedélyezési mappában. A ContactIsOwnerAuthorizationHandler rendszer ellenőrzi, hogy az erőforráson eljáró felhasználóé-e az erőforrás.

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

A ContactIsOwnerAuthorizationHandler meghívja a context.Succeed funkciót, ha az aktuálisan hitelesített felhasználó a kontakt tulajdonosa. Az engedélyezési kezelők általában:

  • Hívd fel context.Succeed, amikor a követelmények teljesülnek.
  • Vissza Task.CompletedTask, ha a követelmények nem teljesülnek. Előzetes Task.CompletedTask vagy context.Success hívás nélkül a context.Fail visszatérés sem nem siker, sem nem kudarc; lehetővé teszi más engedélyezési kezelők futtatását.

Ha explicit módon meg kell jelölnie a hibát, hívja meg a context.Fail függvényt.

Az alkalmazás lehetővé teszi a kapcsolattulajdonosok számára a saját adatuk szerkesztését, törlését és létrehozását. ContactIsOwnerAuthorizationHandler nem kell ellenőriznie a követelményparaméterben átadott műveletet.

Kezelő engedélyezési kezelőjének létrehozása

Hozzon létre egy osztályt ContactManagerAuthorizationHandler az Engedélyezési mappában. A ContactManagerAuthorizationHandler ellenőrzi, hogy az erőforráson eljáró felhasználó vezető-e. Csak a vezetők hagyhatják jóvá vagy utasíthatják el a tartalommódosításokat (új vagy módosított).

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

Rendszergazdai engedélyezési kezelő létrehozása

Hozzon létre egy osztályt ContactAdministratorsAuthorizationHandler az Engedélyezési mappában. Ellenőrzi ContactAdministratorsAuthorizationHandler , hogy az erőforráson eljáró felhasználó rendszergazda-e. A rendszergazda minden műveletet elvégezhet.

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

Az engedélyezési kezelők regisztrálása

Az Entity Framework Core-t használó szolgáltatásokat regisztrálni kell a függőséginjektáláshoz a következőt használva AddScoped. A ContactIsOwnerAuthorizationHandler az ASP.NET Core Identity-t használja, amely az Entity Framework Core-ra épül. Regisztrálja a kezelőket a szolgáltatásgyűjteményben, hogy ContactsController elérhetőek legyenek a függőséginjektálás segítségével. Adja hozzá a következő kódot a ConfigureServicesvégéhez:

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(
    options => options.SignIn.RequireConfirmedAccount = true)
    .AddRoles<IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>();

builder.Services.AddRazorPages();

builder.Services.AddAuthorization(options =>
{
    options.FallbackPolicy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .Build();
});

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

builder.Services.AddSingleton<IAuthorizationHandler,
                      ContactAdministratorsAuthorizationHandler>();

builder.Services.AddSingleton<IAuthorizationHandler,
                      ContactManagerAuthorizationHandler>();

var app = builder.Build();

using (var scope = app.Services.CreateScope())
{
    var services = scope.ServiceProvider;
    var context = services.GetRequiredService<ApplicationDbContext>();
    context.Database.Migrate();
    // requires using Microsoft.Extensions.Configuration;
    // Set password with the Secret Manager tool.
    // dotnet user-secrets set SeedUserPW <pw>

    var testUserPw = builder.Configuration.GetValue<string>("SeedUserPW");

   await SeedData.Initialize(services, testUserPw);
}

ContactAdministratorsAuthorizationHandler és ContactManagerAuthorizationHandler szingletonként lesznek hozzáadva. Egyediek, mert nem használják az EF-t, és minden szükséges információ a Context metódus paraméterében HandleRequirementAsync van.

Támogatás engedélyezése

Ebben a szakaszban frissíti a Razor Pagest, és hozzáad egy műveleti követelményosztályt.

A kontaktműveletek követelményeinek áttekintése

Tekintse át a ContactOperations osztályt. Ez az osztály az alkalmazás által támogatott követelményeket tartalmazza:

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";
    }
}

Alaposztály létrehozása a névjegylapokhoz Razor

Hozzon létre egy alaposztályt, amely tartalmazza a kapcsolatok Razor oldalakon használt szolgáltatásokat. Az alaposztály egy helyre helyezi az inicializálási kódot:

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

Az előző kód:

  • Hozzáadja a szolgáltatást az IAuthorizationService engedélyezési kezelőkhöz való hozzáféréshez.
  • Hozzáadja a IdentityUserManager szolgáltatást.
  • Adja hozzá a ApplicationDbContext.

A CreateModel frissítése

Frissítse a létrehozási oldal modelljét:

  • Konstruktor az DI_BasePageModel alaposztály használatához.
  • OnPostAsync metódus:
    • Adja hozzá a felhasználói azonosítót a Contact modellhez.
    • Hívja meg az engedélyezési kezelőt, hogy ellenőrizze, hogy a felhasználó rendelkezik-e jogosultsággal a névjegyek létrehozására.
using ContactManager.Authorization;
using ContactManager.Data;
using ContactManager.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;

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

        public IActionResult OnGet()
        {
            return Page();
        }

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

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

            Contact.OwnerID = UserManager.GetUserId(User);

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

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

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

Az IndexModel frissítése

Frissítse a metódust, hogy csak a OnGetAsync jóváhagyott névjegyek jelenjenek meg az általános felhasználók számára:

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

Az EditModel frissítése

Adjon hozzá egy engedélyezési kezelőt, amely ellenőrzi, hogy a felhasználó tulajdona-e a kontakt. Mivel az erőforrás-engedélyezés ellenőrzése folyamatban van, az [Authorize] attribútum nem elegendő. Az alkalmazás nem fér hozzá az erőforráshoz az attribútumok kiértékelésekor. Az erőforrás-alapú engedélyezésnek imperatívnak kell lennie. Az ellenőrzéseket el kell végezni, miután az alkalmazás hozzáfér az erőforráshoz, akár az oldalmodellbe töltéssel, akár a saját kezelőjébe történő betöltéssel. Az erőforrást gyakran az erőforráskulcs átadásával érheti el.

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? contact = await Context.Contact.FirstOrDefaultAsync(
                                                         m => m.ContactId == id);
        if (contact == null)
        {
            return NotFound();
        }

        Contact = contact;

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

        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 Forbid();
        }

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

A DeleteModel frissítése

Frissítse a törlési oldalmodellt, hogy az engedélykezelő használatával ellenőrizze, hogy a felhasználó rendelkezik-e törlési engedéllyel a kapcsolaton.

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? _contact = await Context.Contact.FirstOrDefaultAsync(
                                             m => m.ContactId == id);

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

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

        return Page();
    }

    public async Task<IActionResult> OnPostAsync(int 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 Forbid();
        }

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

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

Az engedélyezési szolgáltatás beszúrása a nézetekbe

Jelenleg a felhasználói felületen olyan névjegyek szerkesztési és törlési hivatkozásai jelennek meg, amelyeket a felhasználó nem tud módosítani.

Szúrja be az engedélyezési szolgáltatást a Pages/_ViewImports.cshtml fájlba, hogy az minden nézet számára elérhető legyen:

@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

Az előző jelölőnyelv több using kifejezést ad hozzá.

Frissítse a szerkesztési és törlési hivatkozásokat, Pages/Contacts/Index.cshtml hogy azok csak a megfelelő engedélyekkel rendelkező felhasználók számára legyenek megjelenítve:

@page
@model ContactManager.Pages.Contacts.IndexModel

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

<h1>Index</h1>

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

Warning

Ha olyan felhasználók elől rejt el hivatkozásokat, akiknek nincs engedélye az adatok módosítására, az nem védi az alkalmazást. A hivatkozások elrejtése felhasználóbarátabbá teszi az alkalmazást, ha csak érvényes hivatkozásokat jelenít meg. A felhasználók feltörhetik a létrehozott URL-címeket, hogy szerkesztési és törlési műveleteket kezdeményezhessenek a nem saját adataikon. Az Razor oldalnak vagy a vezérlőnek hozzáférés-ellenőrzéseket kell végrehajtania az adatok védelméhez.

Frissítés részletei

Frissítse a részletek nézetet, hogy a vezetők jóváhagyhassák vagy elutasíthassák a névjegyeket:

        @*Preceding markup omitted for brevity.*@
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Contact.Email)
        </dt>
        <dd class="col-sm-10">
            @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-danger">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>

A részletek lapmodelljének frissítése

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? _contact = await Context.Contact.FirstOrDefaultAsync(m => m.ContactId == id);

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

        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 Forbid();
        }

        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 Forbid();
        }
        contact.Status = status;
        Context.Contact.Update(contact);
        await Context.SaveChangesAsync();

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

Felhasználó hozzáadása vagy eltávolítása szerepkörhöz

A következő problémával kapcsolatos információkért tekintse meg a következőt :

  • Jogosultságok eltávolítása egy felhasználótól. Például elnémít egy felhasználót egy csevegőalkalmazásban.
  • Jogosultságok hozzáadása egy felhasználóhoz.

Különbségek a Challenge és a Forbid között

Ez az alkalmazás beállítja az alapértelmezett szabályzatot, hogy hitelesített felhasználókat követeljen meg. Az alábbi kód lehetővé teszi a névtelen hozzáférést. A névtelen felhasználók megjeleníthetik a Challenge és a Forbid közötti különbségeket.

[AllowAnonymous]
public class Details2Model : DI_BasePageModel
{
    public Details2Model(
        ApplicationDbContext context,
        IAuthorizationService authorizationService,
        UserManager<IdentityUser> userManager)
        : base(context, authorizationService, userManager)
    {
    }

    public Contact Contact { get; set; }

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

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

        if (!User.Identity!.IsAuthenticated)
        {
            return Challenge();
        }

        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 Forbid();
        }

        return Page();
    }
}

Az előző kódban:

  • Ha a felhasználó nincs hitelesítve, a rendszer visszaad egy hibát ChallengeResult . Ha a rendszer visszaad egy ChallengeResult hibát, a rendszer átirányítja a felhasználót a bejelentkezési oldalra.
  • Ha a felhasználó hitelesítése megtörtént, de nincs engedélyezve, a rendszer ad vissza egy hibát ForbidResult . Ha a rendszer visszaad egy hibát ForbidResult , a rendszer átirányítja a felhasználót a hozzáférés megtagadása lapra.

A kész alkalmazás tesztelése

Warning

Ez a cikk a Titkos kulcskezelő eszközzel tárolja a beszúrt felhasználói fiókok jelszavát. A Secret Manager eszköz a helyi fejlesztés során bizalmas adatok tárolására szolgál. Az alkalmazás tesztelési vagy éles környezetben való üzembe helyezésekor használható hitelesítési eljárásokról további információt a Biztonságos hitelesítési folyamatok című témakörben talál.

Ha még nem állított be jelszót a becsomagolt felhasználói fiókokhoz, a Titkos kódkezelő eszközzel állítsa be a jelszót:

  • Válasszon erős jelszót:

    • Legalább 12 karakter hosszú, de 14 vagy több jobb.
    • Nagybetűk, kisbetűk, számok és szimbólumok kombinációja.
    • Nem található szó a szótárban, illetve egy személy, karakter, termék vagy szervezet neveként.
    • Jelentősen eltér a korábbi jelszavaktól.
    • Könnyű neked megjegyezni, de mások számára nehéz kitalálni. Fontolja meg egy olyan emlékezetes kifejezés használatát, mint a "6MonkeysRLooking^".
  • Hajtsa végre a következő parancsot a projekt mappájából, ahol <PW> a jelszó található:

    dotnet user-secrets set SeedUserPW <PW>
    

Ha az alkalmazás névjegyekkel rendelkezik:

  • Törölje a tábla összes rekordját Contact .
  • Indítsa újra az alkalmazást az adatbázis üzembe helyezése érdekében.

Az elkészült alkalmazás tesztelésének egyszerű módja három különböző böngésző (vagy inkognitó/InPrivate-munkamenet) elindítása. Egy böngészőben regisztráljon egy új felhasználót (például test@example.com). Jelentkezzen be minden böngészőbe egy másik felhasználóval. Ellenőrizze a következő műveleteket:

  • A regisztrált felhasználók megtekinthetik az összes jóváhagyott kapcsolattartási adatot.
  • A regisztrált felhasználók szerkeszthetik/törölhetik saját adataikat.
  • A vezetők jóváhagyhatják/elutasíthatják a kapcsolattartási adatokat. A Details nézetben a Jóváhagyás és az Elutasítás gomb látható.
  • A rendszergazdák az összes adatot jóváhagyhatják/elutasíthatják, illetve szerkeszthetik/törölhetik.
User Partnerek jóváhagyása vagy elutasítása Beállítások
test@example.com No Szerkessze és törölje az adatait.
manager@contoso.com Yes Szerkessze és törölje az adatait.
admin@contoso.com Yes Az összes adat szerkesztése és törlése.

Hozzon létre egy kapcsolatot a rendszergazda böngészőjében. Másolja ki a törléshez és szerkesztéshez szükséges URL-címet a rendszergazdával való kapcsolatból. Illessze be ezeket a hivatkozásokat a tesztfelhasználó böngészőjébe, és ellenőrizze, hogy a tesztfelhasználó nem tudja-e végrehajtani ezeket a műveleteket.

A kezdőalkalmazás létrehozása

  • Razor"ContactManager" nevű Pages-alkalmazás létrehozása

    • Hozza létre az alkalmazást egyéni fiókokkal.
    • Nevezze el "ContactManager"-nek, így a névtér megegyezik a mintában használt névtérrel.
    • -uld az SQLite helyett a LocalDB-t adja meg
    dotnet new webapp -o ContactManager -au Individual -uld
    
  • Hozzáadás Models/Contact.cs: secure-data\samples\starter6\ContactManager\Models\Contact.cs

    using System.ComponentModel.DataAnnotations;
    
    namespace ContactManager.Models
    {
        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; }
        }
    }
    
  • Állványozza a Contact modellt.

  • Hozza létre a kezdeti migrálást, és frissítse az adatbázist:

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

Note

Alapértelmezés szerint a telepíteni kívánt .NET bináris fájlok architektúrája a jelenleg futó operációsrendszer-architektúrát jelöli. Egy másik operációsrendszer-architektúra megadásához lásd: dotnet tool install , --arch option. További információ: GitHub-probléma dotnet/AspNetCore.Docs #29262.

  • Frissítse a ContactManager horgonyt a Pages/Shared/_Layout.cshtml fájlban:

    <a class="nav-link text-dark" asp-area="" asp-page="/Contacts/Index">Contact Manager</a>
    
  • Tesztelje az alkalmazást úgy, hogy névjegyet hoz létre, szerkeszt, és töröl

Az adatbázis üzembe helyezése

Adja hozzá a SeedData osztályt az Adatok mappához:

using ContactManager.Models;
using Microsoft.EntityFrameworkCore;

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

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

    }
}

Hívás SeedData.Initialize innen Program.cs:

using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using ContactManager.Data;

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();

var app = builder.Build();

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

    await SeedData.Initialize(services);
}

if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.MapRazorPages();

app.Run();

Tesztelje, hogy az alkalmazás bevetette-e az adatbázist. Ha a kapcsolati adatbázisban vannak sorok, a seed metódus nem fut.

Ez az oktatóanyag bemutatja, hogyan hozhat létre ASP.NET Core-webalkalmazást az engedélyezés által védett felhasználói adatokkal. Megjeleníti a hitelesített (regisztrált) felhasználók által létrehozott névjegyek listáját. Három biztonsági csoport létezik:

  • A regisztrált felhasználók megtekinthetik az összes jóváhagyott adatot, és szerkeszthetik/törölhetik saját adataikat.
  • A vezetők jóváhagyhatják vagy elutasíthatják a kapcsolattartási adatokat. Csak a jóváhagyott névjegyek láthatók a felhasználók számára.
  • A rendszergazdák bármilyen adatot jóváhagyhatnak/elutasíthatnak, illetve szerkeszthetik/törölhetnek.

A dokumentum képei nem felelnek meg pontosan a legújabb sablonoknak.

Az alábbi képen Rick (rick@example.com) felhasználó van bejelentkezve. Rick csak a jóváhagyott névjegyeket tekintheti meg, és a névjegyeihez szerkesztheti/törölheti/újakat hozhat létre. Csak a Rick által létrehozott utolsó rekord jeleníti meg a hivatkozások szerkesztését és törlését . A többi felhasználó nem fogja látni az utolsó rekordot, amíg egy felettes vagy rendszergazda nem módosítja a "Jóváhagyott" állapotot.

Képernyőkép Rick bejelentkezésről

Az alábbi képen manager@contoso.com be van jelentkezve és a menedzser szerepkörében van:

Képernyőkép a bejelentkezésről manager@contoso.com

Az alábbi képen egy kapcsolat kezelőjének részletes nézete látható.

Egy kapcsolattartó menedzser nézete

A Jóváhagyás és elutasítás gomb csak a vezetők és rendszergazdák számára jelenik meg.

Az alábbi képen a admin@contoso.com be van jelentkezve, és az adminisztrátori szerepkörben van.

Képernyőkép a bejelentkezésről admin@contoso.com

A rendszergazda minden jogosultsággal rendelkezik. Elolvashatja, szerkesztheti vagy törölheti a névjegyeket, és módosíthatja a névjegyek állapotát.

Az alkalmazás a következő modell Contact:

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

A minta a következő engedélyezési kezelőket tartalmazza:

  • ContactIsOwnerAuthorizationHandler: Biztosítja, hogy a felhasználók csak az adataikat szerkeszthessék.
  • ContactManagerAuthorizationHandler: Lehetővé teszi a vezetők számára a partnerek jóváhagyását vagy elutasítását.
  • ContactAdministratorsAuthorizationHandler: Lehetővé teszi a rendszergazdák számára a következőt:
    • Partnerek jóváhagyása vagy elutasítása
    • Névjegyek szerkesztése és törlése

Prerequisites

Ez az oktatóanyag speciális. Ismernie kell a következőt:

A kezdő és befejezett alkalmazás

Töltse le a kész alkalmazást. Tesztelje a kész alkalmazást, hogy megismerje annak biztonsági funkcióit.

A kezdőalkalmazás

Töltse le a kezdőalkalmazást .

Futtassa az alkalmazást, koppintson a ContactManager hivatkozásra, és ellenőrizze, hogy létrehozhat, szerkeszthet és törölhet-e partnereket. A kezdőalkalmazás létrehozásához lásd : A kezdőalkalmazás létrehozása.

Felhasználói adatok védelme

Az alábbi szakaszok a biztonságos felhasználói adatalkalmazás létrehozásának összes fő lépését ismertetik. Hasznos lehet, ha a befejezett projektre hivatkozik.

Kapcsolattartási adatok kötése a felhasználóhoz

A ASP.NET Identity felhasználói azonosítóval biztosíthatja, hogy a felhasználók szerkeszthessék az adataikat, más felhasználói adatokat azonban nem. Hozzáadás OwnerID és ContactStatus a Contact modellhez:

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 a felhasználó azonosítója az AspNetUser adatbázis táblájából Identity . A Status mező meghatározza, hogy a kapcsolatokat megtekinthetik-e általában a felhasználók.

Hozzon létre egy új migrálást, és frissítse az adatbázist:

dotnet ef migrations add userID_Status
dotnet ef database update

Szerepkör-szolgáltatások hozzáadása Identity

AddRoles hozzáfűzése a szerepkör-szolgáltatások hozzáadásához:

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>();

Hitelesített felhasználók megkövetelése

Állítsa be a tartalék hitelesítési házirendet a felhasználók hitelesítésének megkövetelésére:

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.AddAuthorization(options =>
    {
        options.FallbackPolicy = new AuthorizationPolicyBuilder()
            .RequireAuthenticatedUser()
            .Build();
    });

Az előző kiemelt kód beállítja a biztonsági hitelesítési szabályzatot. A tartalék hitelesítési szabályzathoz minden felhasználót hitelesíteni kell, kivéve Razor az oldalakat, vezérlőket vagy hitelesítési attribútummal rendelkező műveleti módszereket. Például a Razor lapok, vezérlők vagy műveleti metódusok az alkalmazott [AllowAnonymous] vagy [Authorize(PolicyName="MyPolicy")] hitelesítési attribútumot használják a tartalék hitelesítési szabályzat helyett.

RequireAuthenticatedUser hozzáadja a DenyAnonymousAuthorizationRequirement-et az aktuális példányhoz, amely biztosítja, hogy az aktuális felhasználó hitelesített legyen.

A tartalék hitelesítési szabályzat:

  • A rendszer minden olyan kérésre alkalmazza, amely nem határoz meg explicit módon hitelesítési szabályzatot. A végpont-útválasztás által kiszolgált kérések esetében ez magában foglal minden olyan végpontot, amely nem ad meg engedélyezési attribútumot. Az engedélyezési köztes szoftver ( például statikus fájlok) után más köztes szoftver által kiszolgált kérések esetén ez a szabályzatot az összes kérelemre alkalmazza.

Ha a tartalék hitelesítési szabályzatot úgy állítja be, hogy a felhasználókat hitelesíteni kell, az védi az újonnan hozzáadott Razor oldalakat és vezérlőket. Ha alapértelmezés szerint hitelesítésre van szükség, az biztonságosabb, mint az új vezérlőkre Razor és a Pagesre támaszkodni az [Authorize] attribútum belefoglalásához.

Az AuthorizationOptions osztály AuthorizationOptions.DefaultPolicy-t is tartalmaz. Az DefaultPolicy az a szabályzat, amelyet a [Authorize] attribútummal használnak, ha nincs másik megadva. [Authorize] nem tartalmaz elnevezett szabályzatot, ellentétben a [Authorize(PolicyName="MyPolicy")].

A szabályzatokkal kapcsolatos további információkért lásd a szabályzatalapú engedélyezést a ASP.NET Core-ban.

Az MVC-vezérlők és Razor Pages esetében az összes felhasználó hitelesítése megkövetelésének alternatív módja egy engedélyezési szűrő hozzáadása.

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

Az előző kód egy engedélyezési szűrőt használ, a tartalék szabályzat beállítása végpont-útválasztást használ. A tartalék házirend beállítása az elsődleges módszer az összes felhasználó hitelesítésének megkövetelésére.

Adja hozzá az AllowAnonymous-t a IndexPrivacy lapokhoz, hogy a névtelen felhasználók a regisztráció előtt információt szerezzenek a webhelyről:

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()
        {

        }
    }
}

A tesztfiók konfigurálása

Az SeedData osztály két fiókot hoz létre: rendszergazda és felettes. A Titkos kódkezelő eszközzel jelszót állíthat be ezekhez a fiókokhoz. Állítsa be a jelszót a projektnévtárban (a Program.cs könyvtár):

dotnet user-secrets set SeedUserPW <PW>

Ha nincs megadva erős jelszó, a rendszer kivételt jelez a meghíváskor SeedData.Initialize .

Frissítse Main úgy, hogy a tesztjelszót használja.

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

Hozza létre a tesztfiókokat és frissítse a névjegyeket

Frissítse az Initialize osztály metódusát SeedData a tesztfiókok létrehozásához:

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)
{
    var roleManager = serviceProvider.GetService<RoleManager<IdentityRole>>();

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

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

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

    //if (userManager == null)
    //{
    //    throw new Exception("userManager is null");
    //}

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

Adja hozzá a rendszergazdai felhasználói azonosítót és ContactStatus a névjegyekhez. Az egyik névjegyet "Elküldöttként", a másikat pedig "Elutasítottként" állítsa be. Adja hozzá a felhasználói azonosítót és az állapotot az összes kapcsolathoz. Csak egy kapcsolat jelenik meg:

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

Tulajdonosi, kezelői és rendszergazdai engedélyezési kezelők létrehozása

Hozzon létre egy osztályt ContactIsOwnerAuthorizationHandler az Engedélyezési mappában. A ContactIsOwnerAuthorizationHandler rendszer ellenőrzi, hogy az erőforráson eljáró felhasználóé-e az erőforrás.

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

A ContactIsOwnerAuthorizationHandler meghívja a context.Succeed funkciót, ha az aktuálisan hitelesített felhasználó a kontakt tulajdonosa. Az engedélyezési kezelők általában:

  • Hívd fel context.Succeed, amikor a követelmények teljesülnek.
  • Vissza Task.CompletedTask, ha a követelmények nem teljesülnek. Előzetes Task.CompletedTask vagy context.Success hívás nélkül a context.Fail visszatérés sem nem siker, sem nem kudarc; lehetővé teszi más engedélyezési kezelők futtatását.

Ha explicit módon meg kell jelölnie a hibát, hívja meg a context.Fail függvényt.

Az alkalmazás lehetővé teszi a kapcsolattulajdonosok számára a saját adatuk szerkesztését, törlését és létrehozását. ContactIsOwnerAuthorizationHandler nem kell ellenőriznie a követelményparaméterben átadott műveletet.

Kezelő engedélyezési kezelőjének létrehozása

Hozzon létre egy osztályt ContactManagerAuthorizationHandler az Engedélyezési mappában. A ContactManagerAuthorizationHandler ellenőrzi, hogy az erőforráson eljáró felhasználó vezető-e. Csak a vezetők hagyhatják jóvá vagy utasíthatják el a tartalommódosításokat (új vagy módosított).

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

Rendszergazdai engedélyezési kezelő létrehozása

Hozzon létre egy osztályt ContactAdministratorsAuthorizationHandler az Engedélyezési mappában. Ellenőrzi ContactAdministratorsAuthorizationHandler , hogy az erőforráson eljáró felhasználó rendszergazda-e. A rendszergazda minden műveletet elvégezhet.

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

Az engedélyezési kezelők regisztrálása

Az Entity Framework Core-t használó szolgáltatásokat regisztrálni kell a függőséginjektáláshoz a következőt használva AddScoped. A ContactIsOwnerAuthorizationHandler az ASP.NET Core Identity-t használja, amely az Entity Framework Core-ra épül. Regisztrálja a kezelőket a szolgáltatásgyűjteményben, hogy ContactsController elérhetőek legyenek a függőséginjektálás segítségével. Adja hozzá a következő kódot a ConfigureServicesvégéhez:

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.AddAuthorization(options =>
    {
        options.FallbackPolicy = new AuthorizationPolicyBuilder()
            .RequireAuthenticatedUser()
            .Build();
    });

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

    services.AddSingleton<IAuthorizationHandler,
                          ContactAdministratorsAuthorizationHandler>();

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

ContactAdministratorsAuthorizationHandler és ContactManagerAuthorizationHandler szingletonként lesznek hozzáadva. Egyediek, mert nem használják az EF-t, és minden szükséges információ a Context metódus paraméterében HandleRequirementAsync van.

Támogatás engedélyezése

Ebben a szakaszban frissíti a Razor Pagest, és hozzáad egy műveleti követelményosztályt.

A kontaktműveletek követelményeinek áttekintése

Tekintse át a ContactOperations osztályt. Ez az osztály az alkalmazás által támogatott követelményeket tartalmazza:

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";
    }
}

Alaposztály létrehozása a névjegylapokhoz Razor

Hozzon létre egy alaposztályt, amely tartalmazza a kapcsolatok Razor oldalakon használt szolgáltatásokat. Az alaposztály egy helyre helyezi az inicializálási kódot:

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

Az előző kód:

  • Hozzáadja a szolgáltatást az IAuthorizationService engedélyezési kezelőkhöz való hozzáféréshez.
  • Hozzáadja a IdentityUserManager szolgáltatást.
  • Adja hozzá a ApplicationDbContext.

A CreateModel frissítése

Frissítse a lapmodell-konstruktort az DI_BasePageModel alaposztály használatára:

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

Frissítse a metódust a következőre: CreateModel.OnPostAsync

  • Adja hozzá a felhasználói azonosítót a Contact modellhez.
  • Hívja meg az engedélyezési kezelőt, hogy ellenőrizze, hogy a felhasználó rendelkezik-e jogosultsággal a névjegyek létrehozására.
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 Forbid();
    }

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

    return RedirectToPage("./Index");
}

Az IndexModel frissítése

Frissítse a metódust, hogy csak a OnGetAsync jóváhagyott névjegyek jelenjenek meg az általános felhasználók számára:

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

Az EditModel frissítése

Adjon hozzá egy engedélyezési kezelőt, amely ellenőrzi, hogy a felhasználó tulajdona-e a kontakt. Mivel az erőforrás-engedélyezés ellenőrzése folyamatban van, az [Authorize] attribútum nem elegendő. Az alkalmazás nem fér hozzá az erőforráshoz az attribútumok kiértékelésekor. Az erőforrás-alapú engedélyezésnek imperatívnak kell lennie. Az ellenőrzéseket el kell végezni, miután az alkalmazás hozzáfér az erőforráshoz, akár az oldalmodellbe töltéssel, akár a saját kezelőjébe történő betöltéssel. Az erőforrást gyakran az erőforráskulcs átadásával érheti el.

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 Forbid();
        }

        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 Forbid();
        }

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

A DeleteModel frissítése

Frissítse a törlési oldalmodellt, hogy az engedélykezelő használatával ellenőrizze, hogy a felhasználó rendelkezik-e törlési engedéllyel a kapcsolaton.

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 Forbid();
        }

        return Page();
    }

    public async Task<IActionResult> OnPostAsync(int 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 Forbid();
        }

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

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

Az engedélyezési szolgáltatás beszúrása a nézetekbe

Jelenleg a felhasználói felületen olyan névjegyek szerkesztési és törlési hivatkozásai jelennek meg, amelyeket a felhasználó nem tud módosítani.

Szúrja be az engedélyezési szolgáltatást a Pages/_ViewImports.cshtml fájlba, hogy az minden nézet számára elérhető legyen:

@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

Az előző jelölőnyelv több using kifejezést ad hozzá.

Frissítse a szerkesztési és törlési hivatkozásokat, Pages/Contacts/Index.cshtml hogy azok csak a megfelelő engedélyekkel rendelkező felhasználók számára legyenek megjelenítve:

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

Warning

Ha olyan felhasználók elől rejt el hivatkozásokat, akiknek nincs engedélye az adatok módosítására, az nem védi az alkalmazást. A hivatkozások elrejtése felhasználóbarátabbá teszi az alkalmazást, ha csak érvényes hivatkozásokat jelenít meg. A felhasználók feltörhetik a létrehozott URL-címeket, hogy szerkesztési és törlési műveleteket kezdeményezhessenek a nem saját adataikon. Az Razor oldalnak vagy a vezérlőnek hozzáférés-ellenőrzéseket kell végrehajtania az adatok védelméhez.

Frissítés részletei

Frissítse a részletek nézetet, hogy a vezetők jóváhagyhassák vagy elutasíthassák a névjegyeket:

        @*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-danger">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>

Frissítse a részletek lapmodellt:

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 Forbid();
        }

        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 Forbid();
        }
        contact.Status = status;
        Context.Contact.Update(contact);
        await Context.SaveChangesAsync();

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

Felhasználó hozzáadása vagy eltávolítása szerepkörhöz

A következő problémával kapcsolatos információkért tekintse meg a következőt :

  • Jogosultságok eltávolítása egy felhasználótól. Például elnémít egy felhasználót egy csevegőalkalmazásban.
  • Jogosultságok hozzáadása egy felhasználóhoz.

Különbségek a Challenge és a Forbid között

Ez az alkalmazás beállítja az alapértelmezett szabályzatot, hogy hitelesített felhasználókat követeljen meg. Az alábbi kód lehetővé teszi a névtelen hozzáférést. A névtelen felhasználók megjeleníthetik a Challenge és a Forbid közötti különbségeket.

[AllowAnonymous]
public class Details2Model : DI_BasePageModel
{
    public Details2Model(
        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();
        }

        if (!User.Identity.IsAuthenticated)
        {
            return Challenge();
        }

        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 Forbid();
        }

        return Page();
    }
}

Az előző kódban:

  • Ha a felhasználó nincs hitelesítve, a rendszer visszaad egy hibát ChallengeResult . Ha a rendszer visszaad egy ChallengeResult hibát, a rendszer átirányítja a felhasználót a bejelentkezési oldalra.
  • Ha a felhasználó hitelesítése megtörtént, de nincs engedélyezve, a rendszer ad vissza egy hibát ForbidResult . Ha a rendszer visszaad egy hibát ForbidResult , a rendszer átirányítja a felhasználót a hozzáférés megtagadása lapra.

A kész alkalmazás tesztelése

Ha még nem állított be jelszót a becsomagolt felhasználói fiókokhoz, a Titkos kódkezelő eszközzel állítsa be a jelszót:

  • Válasszon erős jelszót: Használjon nyolc vagy több karaktert, és legalább egy nagybetűs karaktert, számot és szimbólumot. Például Passw0rd! megfelel az erős jelszókövetelményeknek.

  • Hajtsa végre a következő parancsot a projekt mappájából, ahol <PW> a jelszó található:

    dotnet user-secrets set SeedUserPW <PW>
    

Ha az alkalmazás névjegyekkel rendelkezik:

  • Törölje a tábla összes rekordját Contact .
  • Indítsa újra az alkalmazást az adatbázis üzembe helyezése érdekében.

Az elkészült alkalmazás tesztelésének egyszerű módja három különböző böngésző (vagy inkognitó/InPrivate-munkamenet) elindítása. Egy böngészőben regisztráljon egy új felhasználót (például test@example.com). Jelentkezzen be minden böngészőbe egy másik felhasználóval. Ellenőrizze a következő műveleteket:

  • A regisztrált felhasználók megtekinthetik az összes jóváhagyott kapcsolattartási adatot.
  • A regisztrált felhasználók szerkeszthetik/törölhetik saját adataikat.
  • A vezetők jóváhagyhatják/elutasíthatják a kapcsolattartási adatokat. A Details nézetben a Jóváhagyás és az Elutasítás gomb látható.
  • A rendszergazdák az összes adatot jóváhagyhatják/elutasíthatják, illetve szerkeszthetik/törölhetik.
User Az alkalmazás által bevetett Beállítások
test@example.com No Saját adatok szerkesztése/törlése.
manager@contoso.com Yes Saját adatok jóváhagyása/elutasítása és szerkesztése/törlése.
admin@contoso.com Yes Az összes adat jóváhagyása/elvetése és szerkesztése/törlése.

Hozzon létre egy kapcsolatot a rendszergazda böngészőjében. Másolja ki a törléshez és szerkesztéshez szükséges URL-címet a rendszergazdával való kapcsolatból. Illessze be ezeket a hivatkozásokat a tesztfelhasználó böngészőjébe, és ellenőrizze, hogy a tesztfelhasználó nem tudja-e végrehajtani ezeket a műveleteket.

A kezdőalkalmazás létrehozása

  • Razor"ContactManager" nevű Pages-alkalmazás létrehozása

    • Hozza létre az alkalmazást egyéni fiókokkal.
    • Nevezze el "ContactManager"-nek, így a névtér megegyezik a mintában használt névtérrel.
    • -uld az SQLite helyett a LocalDB-t adja meg
    dotnet new webapp -o ContactManager -au Individual -uld
    
  • Adja hozzá 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; }
    }
    
  • Állványozza a Contact modellt.

  • Hozza létre a kezdeti migrálást, és frissítse az adatbázist:

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

Note

Alapértelmezés szerint a telepíteni kívánt .NET bináris fájlok architektúrája a jelenleg futó operációsrendszer-architektúrát jelöli. Egy másik operációsrendszer-architektúra megadásához lásd: dotnet tool install , --arch option. További információ: GitHub-probléma dotnet/AspNetCore.Docs #29262.

Ha hibát tapasztal a paranccsal, tekintse meg ezt a dotnet aspnet-codegenerator razorpageGitHub-problémát.

  • Frissítse a ContactManager horgonyt a Pages/Shared/_Layout.cshtml fájlban:
<a class="navbar-brand" asp-area="" asp-page="/Contacts/Index">ContactManager</a>
  • Tesztelje az alkalmazást úgy, hogy névjegyet hoz létre, szerkeszt, és töröl

Az adatbázis üzembe helyezése

Adja hozzá a SeedData osztályt az Adatok mappához:

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

    }
}

Hívás SeedData.Initialize innen 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>();
                });
    }
}

Tesztelje, hogy az alkalmazás bevetette-e az adatbázist. Ha a kapcsolati adatbázisban vannak sorok, a seed metódus nem fut.

További erőforrások