Oefening: claims gebruiken met op beleid gebaseerde autorisatie

Voltooid

In de vorige les hebt u het verschil tussen verificatie en autorisatie geleerd. U hebt ook geleerd hoe claims worden gebruikt door beleid voor autorisatie. In deze eenheid gebruikt u Identiteit voor het opslaan van claims en het toepassen van beleid voor voorwaardelijke toegang.

De pizzalijst beveiligen

U hebt een nieuwe vereiste ontvangen dat de pagina Pizzalijst alleen zichtbaar moet zijn voor geverifieerde gebruikers. Daarnaast mogen alleen beheerders pizza's maken en verwijderen. Laten we het vergrendelen.

  1. Pas in Pagina's/Pizza.cshtml.cs de volgende wijzigingen toe:

    1. Voeg een [Authorize] kenmerk toe aan de PizzaModel klasse.

      [Authorize]
      public class PizzaModel : PageModel
      

      Het kenmerk beschrijft de vereisten voor gebruikersautorisatie voor de pagina. In dit geval is de enige vereiste dat de gebruiker moet worden geverifieerd. Anonieme gebruikers mogen de pagina niet bekijken en worden omgeleid naar de aanmeldingspagina.

    2. Los de verwijzing naar Authorize op door de volgende regel toe te voegen aan de using instructies boven aan het bestand:

      using Microsoft.AspNetCore.Authorization;
      
    3. Voeg de volgende eigenschap toe aan de klasse PizzaModel:

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

      De voorgaande code bepaalt of de geverifieerde gebruiker een IsAdmin-claim met waarde True heeft. De code haalt informatie op over de geverifieerde gebruiker van de HttpContext bovenliggende PageModel klasse. Het resultaat van deze evaluatie is toegankelijk via een eigenschap alleen-lezen met de naam IsAdmin.

    4. Toevoegen if (!IsAdmin) return Forbid(); aan het begin van zowel de als OnPost de OnPostDelete methoden:

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

      In de volgende stap verbergt u de elementen van de gebruikersinterface voor het maken/verwijderen van gebruikersinterfaces voor niet-beheerders. Dat voorkomt niet dat een kwaadwillende persoon met een hulpprogramma, zoals HttpRepl of curl, rechtstreeks toegang krijgt tot deze eindpunten. Als u deze controle toevoegt, zorgt u ervoor dat als dit wordt geprobeerd, een HTTP 403-statuscode wordt geretourneerd.

  2. Voeg in Pages/Pizza.cshtml controles toe om ui-elementen van beheerders te verbergen voor niet-beheerders:

    Nieuw pizzaformulier verbergen

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

    Knop Pizza verwijderen verbergen

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

    De voorgaande wijzigingen zorgen ervoor dat ui-elementen die alleen toegankelijk moeten zijn voor beheerders alleen worden weergegeven wanneer de geverifieerde gebruiker een beheerder is.

Een autorisatiebeleid toepassen

Er is nog één ding dat je moet vergrendelen. Er is een pagina die alleen toegankelijk moet zijn voor beheerders, met de naam Pages/AdminsOnly.cshtml. Laten we een beleid maken om de IsAdmin=True claim te controleren.

  1. Breng in Program.cs de volgende wijzigingen aan:

    1. Neem de volgende gemarkeerde code op:

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

      Met de voorgaande code wordt autorisatiebeleid met de naam Admin gemaakt. Het beleid vereist dat de gebruiker is geverifieerd en dat er een IsAdmin-claim is ingesteld voor True.

    2. Wijzig de aanroep als AddRazorPages volgt:

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

      De AuthorizePage methodeaanroep beveiligt de route /AdminsOnly Razor Page door het Admin beleid toe te passen. Geverifieerde gebruikers die niet voldoen aan de beleidsvereisten, krijgen een Toegang geweigerd-bericht.

      Tip

      U kunt in plaats daarvan ook AdminsOnly.cshtml.cs wijzigen. In dat geval voegt u dit toe [Authorize(Policy = "Admin")] als een kenmerk in de AdminsOnlyModel klasse. Een voordeel van de AuthorizePage hierboven weergegeven benadering is dat de Razor-pagina die wordt beveiligd, geen wijzigingen vereist. Het autorisatie-aspect wordt in plaats daarvan beheerd in Program.cs.

  2. Neem in Pages/Shared/_Layout.cshtml de volgende wijzigingen op:

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

    Met de voorgaande wijziging wordt de koppeling Beheerder in de koptekst voorwaardelijk verborgen als de gebruiker geen beheerder is. De eigenschap van de Context klasse wordt gebruikt RazorPage voor toegang tot de HttpContext informatie over de geverifieerde gebruiker.

De IsAdmin claim toevoegen aan een gebruiker

Om te bepalen welke gebruikers de IsAdmin=True claim moeten ontvangen, gaat uw app vertrouwen op een bevestigd e-mailadres om de beheerder te identificeren.

  1. Voeg in appsettings.json de gemarkeerde eigenschap toe:

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

    Dit is het bevestigd e-mailadres dat de claim krijgt toegewezen.

  2. Breng in gebieden/identiteit/pagina's/account/ConfirmEmail.cshtml.cs de volgende wijzigingen aan:

    1. Neem de volgende gemarkeerde code op:

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

      De voorgaande wijziging wijzigt de constructor om een IConfiguration van de IoC-container te ontvangen. De IConfiguration bevat waarden uit appsettings.json en wordt toegewezen aan een alleen-lezen eigenschap met de naam Configuration.

    2. Pas de gemarkeerde wijzigingen toe op de methode OnGetAsync:

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

      In de voorgaande code:

      • De AdminEmail tekenreeks wordt gelezen uit de Configuration eigenschap en toegewezen aan adminEmail.
      • De operator ?? null-coalescing wordt gebruikt om ervoor te zorgen dat adminEmail deze is ingesteld string.Empty op als er geen overeenkomstige waarde in appsettings.json is.
      • Als het e-mailadres van de gebruiker is bevestigd:
        • Het adres van de gebruiker wordt vergeleken met adminEmail. string.Compare() wordt gebruikt voor een niet-hoofdlettergevoelige vergelijking.
        • De methode UserManager van de klasse AddClaimAsync wordt aangeroepen om een IsAdmin-claim in de tabel AspNetUserClaims op te slaan.
    3. Voeg de volgende code boven in het bestand toe. Hiermee worden de Claim klasseverwijzingen in de OnGetAsync methode omgezet:

      using System.Security.Claims;
      

De beheerdersclaim testen

Laten we een laatste test uitvoeren om de nieuwe beheerdersfunctionaliteit te controleren.

  1. Zorg ervoor dat u al uw wijzigingen hebt opgeslagen.

  2. Voer de app uit met dotnet run.

  3. Navigeer naar uw app en meld u aan met een bestaande gebruiker als u nog niet bent aangemeld. Selecteer Pizzalijst in de koptekst. U ziet dat de gebruiker geen UI-elementen bevat om pizza's te verwijderen of te maken.

  4. Er is geen koppeling beheerders in de koptekst. Navigeer in de adresbalk van de browser rechtstreeks naar de pagina AdminsOnly . Vervang /Pizza in de URL door /AdminsOnly.

    Het is de gebruiker verboden om naar de pagina te navigeren. Er wordt een Toegang geweigerd-bericht weergegeven.

  5. Selecteer Afmelden.

  6. Registreer een nieuwe gebruiker met het adres admin@contosopizza.com.

  7. Bevestig net als voorheen het e-mailadres van de nieuwe gebruiker en meld u aan.

  8. Nadat u zich hebt aangemeld met de nieuwe gebruiker met beheerdersrechten, selecteert u de koppeling pizzalijst in de koptekst.

    De gebruiker met beheerdersrechten kan pizza's maken en verwijderen.

  9. Selecteer de koppeling Beheerders in de koptekst.

    De pagina AdminsOnly wordt weergegeven.

De tabel AspNetUserClaims controleren

Voer met behulp van de SQL Server-extensie in VS Code de volgende query uit:

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

Er wordt een tabblad met resultaten weergegeven die er ongeveer als volgt uitzien:

E-mailen ClaimType ClaimValue
admin@contosopizza.com IsAdmin Waar

De claim IsAdmin wordt opgeslagen als een sleutel-waardepaar in de tabel AspNetUserClaims. Het record AspNetUserClaims is gekoppeld aan het gebruikersrecord in de tabel AspNetUsers.

Samenvatting

In deze les hebt u de app gewijzigd om claims op te slaan en beleidsregels toe te passen voor voorwaardelijke toegang.