Gyakorlat – Jogcímek használata szabályzatalapú engedélyezéssel

Befejeződött

Az előző leckében megismerte a hitelesítés és az engedélyezés közötti különbséget. Azt is megtanulta, hogyan használják a jogcímeket a szabályzatok az engedélyezéshez. Ebben a leckében az Identity használatával jogcímeket tárolhat, és szabályzatokat alkalmazhat a feltételes hozzáféréshez.

A pizzalista védelme

Új követelményt kapott, hogy a Pizzalista lap csak a hitelesített felhasználók számára legyen látható. Emellett csak rendszergazdák hozhatnak létre és törölhetnek pizzákat. Zárjuk le.

  1. A Pages/Pizza.cshtml.cs fájlban alkalmazza a következő módosításokat:

    1. Adjon hozzá egy [Authorize] attribútumot a PizzaModel osztályhoz.

      [Authorize]
      public class PizzaModel : PageModel
      

      Az attribútum a lap felhasználói engedélyezési követelményeit ismerteti. Ebben az esetben nincs más követelmény, csak hogy a felhasználó hitelesítve legyen. A névtelen felhasználók nem tekinthetik meg a lapot, és a rendszer átirányítja a bejelentkezési lapra.

    2. A hivatkozás feloldásához Authorize adja hozzá a következő sort a using fájl tetején található irányelvekhez:

      using Microsoft.AspNetCore.Authorization;
      
    3. Adja hozzá a következő tulajdonságot a PizzaModel osztályhoz:

      [Authorize]
      public class PizzaModel : PageModel
      {
          public bool IsAdmin => HttpContext.User.HasClaim("IsAdmin", bool.TrueString);
      
          public List<Pizza> pizzas = new();
      

      A fenti kód határozza meg, hogy a hitelesített felhasználó True értékű IsAdmin jogcímmel rendelkezik-e. Ennek a kiértékelésnek az eredménye egy IsAdmin nevű csak olvasható tulajdonságban érhető el.

    4. Adja hozzá if (!IsAdmin) return Forbid(); a és OnPostDeleteaOnPost metódus elejéhez:

      public IActionResult OnPost()
      {
          if (!IsAdmin) return Forbid();
          if (!ModelState.IsValid)
          {
              return Page();
          }
          PizzaService.Add(NewPizza);
          return RedirectToAction("Get");
      }
      
      public IActionResult OnPostDelete(int id)
      {
          if (!IsAdmin) return Forbid();
          PizzaService.Delete(id);
          return RedirectToAction("Get");
      }
      

      A következő lépésben elrejti a létrehozási/törlési felhasználói felület elemeit a nem rendszergazdák számára. Ez nem akadályozza meg, hogy egy támadó egy olyan eszközzel, mint a HttpRepl vagy a Postman közvetlenül hozzáférjen ezekhez a végpontokhoz. Az ellenőrzés hozzáadásával biztosítható, hogy ha ez megkísérlése történik, a rendszer HTTP 403-állapotkódot ad vissza.

  2. A Pages/Pizza.cshtml fájlban adjon hozzá ellenőrzéseket a rendszergazdai felhasználói felület elemeinek elrejtéséhez a nem rendszergazdák elől:

    Új pizza űrlap elrejtése

    <h1>Pizza List 🍕</h1>
    @if (Model.IsAdmin)
    {
    <form method="post" class="card p-3">
        <div class="row">
            <div asp-validation-summary="All"></div>
        </div>
        <div class="form-group mb-0 align-middle">
            <label asp-for="NewPizza.Name">Name</label>
            <input type="text" asp-for="NewPizza.Name" class="mr-5">
            <label asp-for="NewPizza.Size">Size</label>
            <select asp-for="NewPizza.Size" asp-items="Html.GetEnumSelectList<PizzaSize>()" class="mr-5"></select>
            <label asp-for="NewPizza.Price"></label>
            <input asp-for="NewPizza.Price" class="mr-5" />
            <label asp-for="NewPizza.IsGlutenFree">Gluten Free</label>
            <input type="checkbox" asp-for="NewPizza.IsGlutenFree" class="mr-5">
            <button class="btn btn-primary">Add</button>
        </div>
    </form>
    }
    

    A Pizza törlése gomb elrejtése

    <table class="table mt-5">
        <thead>
            <tr>
                <th scope="col">Name</th>
                <th scope="col">Price</th>
                <th scope="col">Size</th>
                <th scope="col">Gluten Free</th>
                @if (Model.IsAdmin)
                {
                <th scope="col">Delete</th>
                }
            </tr>
        </thead>
        @foreach (var pizza in Model.pizzas)
        {
            <tr>
                <td>@pizza.Name</td>
                <td>@($"{pizza.Price:C}")</td>
                <td>@pizza.Size</td>
                <td>@Model.GlutenFreeText(pizza)</td>
                @if (Model.IsAdmin)
                {
                <td>
                    <form method="post" asp-page-handler="Delete" asp-route-id="@pizza.Id">
                        <button class="btn btn-danger">Delete</button>
                    </form>
                </td>
                }
            </tr>
        }
    </table>
    

    Az előző módosítások miatt a csak rendszergazdák számára elérhető felhasználói felületi elemek csak akkor lesznek megjelenítve, ha a hitelesített felhasználó rendszergazda.

Engedélyezési szabályzat alkalmazása

Van még egy dolog, amit le kellene záratnia. Van egy lap, amely csak a rendszergazdák számára érhető el, egyszerűen Pages/AdminsOnly.cshtml néven. Hozzunk létre egy szabályzatot a IsAdmin=True jogcím ellenőrzéséhez.

  1. A Program.cs fájlban végezze el a következő módosításokat:

    1. Szúrja be az alább kiemelt kódot:

      // Add services to the container.
      builder.Services.AddRazorPages();
      builder.Services.AddTransient<IEmailSender, EmailSender>();
      builder.Services.AddSingleton(new QRCodeService(new QRCodeGenerator()));
      builder.Services.AddAuthorization(options =>
          options.AddPolicy("Admin", policy =>
              policy.RequireAuthenticatedUser()
                  .RequireClaim("IsAdmin", bool.TrueString)));
      
      var app = builder.Build();
      

      A fenti kód egy Admin nevű engedélyezési szabályzatot definiál. A szabályzat megköveteli, hogy a felhasználó hitelesítve legyen, és True értékű IsAdmin jogcímmel rendelkezzen.

    2. Módosítsa a hívást AddRazorPages a következőre:

      builder.Services.AddRazorPages(options =>
          options.Conventions.AuthorizePage("/AdminsOnly", "Admin"));
      

      A AuthorizePage metódushívás a szabályzat alkalmazásával biztosítja a /AdminsOnly Razor-oldal útvonalát Admin . Azoknak a hitelesített felhasználóknak, akik nem tesznek eleget a szabályzat követelményeinek, a Hozzáférés megtagadva üzenet jelenik meg.

      Tipp

      Másik lehetőségként módosíthatja az AdminsOnly.cshtml.cs fájlt. Ebben az esetben attribútumként kell hozzáadnia [Authorize(Policy = "Admin")] a AdminsOnlyModel osztályhoz. A fent bemutatott megközelítés egyik előnye AuthorizePage , hogy a védelem alatt álló Razor-oldal nem igényel módosításokat. Az engedélyezési szempont kezelése ehelyett a Program.cs fájlban történik.

  2. A Pages/Shared/_Layout.cshtml fájlban foglalja bele a következő módosításokat:

    <ul class="navbar-nav flex-grow-1">
        <li class="nav-item">
            <a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
        </li>
        <li class="nav-item">
            <a class="nav-link text-dark" asp-area="" asp-page="/Pizza">Pizza List</a>
        </li>
        <li class="nav-item">
            <a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a>
        </li>
        @if (Context.User.HasClaim("IsAdmin", bool.TrueString))
        {
        <li class="nav-item">
            <a class="nav-link text-dark" asp-area="" asp-page="/AdminsOnly">Admins</a>
        </li>
        }
    </ul>
    

    Az előző módosítás feltételesen elrejti az Rendszergazda hivatkozást a fejlécben, ha a felhasználó nem rendszergazda.

IsAdmin Jogcím hozzáadása egy felhasználóhoz

Annak megállapításához, hogy mely felhasználóknak kell megkapniuk a IsAdmin=True jogcímet, az alkalmazás egy megerősített e-mail-címre fog támaszkodni a rendszergazda azonosításához.

  1. Az appsettings.json fájlban adja hozzá a kiemelt tulajdonságot:

    {
      "AdminEmail" : "admin@contosopizza.com",
      "Logging": {
    

    Ez az a megerősített e-mail-cím, amely hozzárendeli a jogcímet.

  2. A Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs területen hajtsa végre a következő módosításokat:

    1. Szúrja be az alább kiemelt kódot:

      public class ConfirmEmailModel : PageModel
      {
          private readonly UserManager<RazorPagesPizzaUser> _userManager;
          private readonly IConfiguration Configuration;
      
          public ConfirmEmailModel(UserManager<RazorPagesPizzaUser> userManager,
                                      IConfiguration configuration)
          {
              _userManager = userManager;
              Configuration = configuration;
          }
      
      

      Az előző módosítás módosítja a konstruktort, hogy az IoC-tárolótól kapjon egy IConfiguration értéket. A IConfiguration az appsettings.json fájl értékeit tartalmazza, és egy nevű írásvédett tulajdonsághoz Configurationvan rendelve.

    2. Alkalmazza a kiemelt módosításokat az OnGetAsync metódusra:

      public async Task<IActionResult> OnGetAsync(string userId, string code)
      {
          if (userId == null || code == null)
          {
              return RedirectToPage("/Index");
          }
      
          var user = await _userManager.FindByIdAsync(userId);
          if (user == null)
          {
              return NotFound($"Unable to load user with ID '{userId}'.");
          }
      
          code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code));
          var result = await _userManager.ConfirmEmailAsync(user, code);
          StatusMessage = result.Succeeded ? "Thank you for confirming your email." : "Error confirming your email.";
      
          var adminEmail = Configuration["AdminEmail"] ?? string.Empty;
          if(result.Succeeded)
          {
              var isAdmin = string.Compare(user.Email, adminEmail, true) == 0 ? true : false;
              await _userManager.AddClaimAsync(user, 
                  new Claim("IsAdmin", isAdmin.ToString()));
          }
      
          return Page();
      }
      

      A fenti kód a következőket végzi el:

      • A AdminEmail sztring beolvasva a Configuration tulajdonságból, és hozzá lesz rendelve a következőhöz adminEmail: .
      • A null-coalescing operátort ?? használja annak ellenőrzésére adminEmail , hogy string.Empty az érték értékre van-e állítva, ha nincs megfelelő érték az appsettings.json fájlban.
      • Ha a felhasználó e-mail-címe sikeresen megerősítést nyert:
        • A rendszer összehasonlítja a felhasználó címét a következőhöz adminEmail: . string.Compare() a kis- és nagybetűket nem megkülönböztető összehasonlításhoz használatos.
        • A UserManager osztály AddClaimAsync metódusa lesz meghívva egy IsAdmin jogcím mentéséhez az AspNetUserClaims táblába.
    3. A fájl elejére szúrja be a következő kódot. Feloldja az Claim osztályhivatkozásokat a OnGetAsync metódusban:

      using System.Security.Claims;
      

Adminisztrátori jogcím tesztelése

Lássunk egy utolsó tesztet az új rendszergazdai funkció ellenőrzéséhez.

  1. Győződjön meg arról, hogy az összes módosítást mentette.

  2. Futtassa az alkalmazást a következővel: dotnet run.

  3. Nyissa meg az alkalmazást, és jelentkezzen be egy meglévő felhasználóval, ha még nincs bejelentkezve. Válassza a Pizzalista elemet a fejlécből. Figyelje meg, hogy a felhasználó nem mutat be felhasználói felületi elemeket a pizzák törléséhez vagy létrehozásához.

  4. A fejlécben nincs Rendszergazda hivatkozás. A böngésző címsorában lépjen közvetlenül a RendszergazdákOnly lapra. Cserélje le /Pizza az URL-címet a következőre: /AdminsOnly.

    A felhasználó számára tiltva van az oldal megnyitása. A Hozzáférés megtagadva üzenet jelenik meg.

  5. Válassza a Kijelentkezés lehetőséget.

  6. Regisztráljon egy új felhasználót a következő címmel admin@contosopizza.com: .

  7. A korábbiakhoz hasonlóan erősítse meg az új felhasználó e-mail-címét, és jelentkezzen be.

  8. Miután bejelentkezett az új rendszergazda felhasználóval, válassza a Pizzalista hivatkozást a fejlécben.

    A rendszergazda létrehozhat és törölhet pizzákat.

  9. A fejlécben válassza a Rendszergazdák hivatkozást.

    Megjelenik az AdminsOnly lap.

Az AspNetUserClaims tábla vizsgálata

Futtassa a következő lekérdezést a VS Code SQL Server bővítményével:

SELECT u.Email, c.ClaimType, c.ClaimValue
FROM dbo.AspNetUserClaims AS c
    INNER JOIN dbo.AspNetUsers AS u
    ON c.UserId = u.Id

Ekkor megjelenik egy lap az alábbihoz hasonló eredményekkel:

E-mail Jogcímtípus ClaimValue
admin@contosopizza.com IsAdmin Igaz

Az IsAdmin jogcím kulcs-érték párként van tárolva az AspNetUserClaims táblában. Az AspNetUserClaims rekord az AspNetUsers táblában lévő felhasználói rekorddal van társítva.

Összefoglalás

Ebben az egységben úgy módosította az alkalmazást, hogy jogcímeket tároljon, és szabályzatokat alkalmazzon a feltételes hozzáféréshez.