Cvičení – použití deklarací identity s autorizací na základě zásad
V předchozí lekci jste zjistili rozdíl mezi ověřováním a autorizací. Také jste se dozvěděli, jak zásady používají deklarace identity k autorizaci. V této lekci použijete identitu k ukládání deklarací identity a použití zásad pro podmíněný přístup.
Zabezpečení seznamu pizzy
Dostali jste nový požadavek, aby stránka Seznam pizzerie byla viditelná jenom ověřeným uživatelům. Kromě toho můžou pizzu vytvářet a odstraňovat jenom správci. Pojďme ho zamknout.
V pages/Pizza.cshtml.cs použijte následující změny:
[Authorize]
Přidejte atribut doPizzaModel
třídy .[Authorize] public class PizzaModel : PageModel
Atribut popisuje požadavky na autorizaci uživatelů pro stránku. V tomto případě neexistují kromě ověření uživatele žádné požadavky. Anonymní uživatelé nemůžou stránku zobrazit a jsou přesměrováni na přihlašovací stránku.
Vyřešte odkaz na
Authorize
přidáním následujícího řádku dousing
direktiv na začátek souboru:using Microsoft.AspNetCore.Authorization;
Do třídy
PizzaModel
přidejte následující vlastnost:[Authorize] public class PizzaModel : PageModel { public bool IsAdmin => HttpContext.User.HasClaim("IsAdmin", bool.TrueString); public List<Pizza> pizzas = new();
Předchozí kód určuje, jestli má ověřený uživatel deklaraci identity
IsAdmin
s hodnotouTrue
. Výsledek tohoto vyhodnocení je přístupný přes vlastnost jen pro čtení s názvemIsAdmin
.Na začátek metody a
OnPost
OnPostDelete
přidejteif (!IsAdmin) return Forbid();
: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"); }
V dalším kroku skryjete prvky uživatelského rozhraní pro vytváření a odstraňování pro uživatele, kteří nejsou správci. To nezabrání nežádoucímu člověku s nástrojem, jako je HttpRepl nebo Postman, v přímém přístupu k těmto koncovým bodům. Přidání této kontroly zajistí, že se při tomto pokusu vrátí stavový kód HTTP 403.
V Pages/Pizza.cshtml přidejte kontroly, které skryjí prvky uživatelského rozhraní správce před nesprávci:
Skrýt formulář Nová pizza
<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> }
Skrýt tlačítko Odstranit pizzu
<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>
Předchozí změny způsobí, že prvky uživatelského rozhraní, které by měly být přístupné pouze správcům, se vykreslují jenom v případě, že je ověřený uživatel správcem.
Použití zásad autorizace
Je tu ještě jedna věc, kterou byste měli zamknout. Existuje stránka, která by měla být přístupná jenom správcům a má praktický název Pages/AdminsOnly.cshtml. Pojďme vytvořit zásadu pro kontrolu IsAdmin=True
deklarace identity.
V souboru Program.cs proveďte následující změny:
Začleňte následující zvýrazněný kód:
// 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();
Předchozí kód definuje zásady autorizace s názvem
Admin
. Tyto zásady vyžadují, aby byl uživatel ověřený a deklaraci identityIsAdmin
měl nastavenou naTrue
.Upravte volání
AddRazorPages
metody následujícím způsobem:builder.Services.AddRazorPages(options => options.Conventions.AuthorizePage("/AdminsOnly", "Admin"));
Volání
AuthorizePage
metody zabezpečí trasu /AdminsOnly Razor Page použitímAdmin
zásad. Ověřeným uživatelům, kteří nesplňují požadavky zásad, se zobrazí zpráva o odepření přístupu.Tip
Případně byste místo toho mohli upravit Soubor AdminsOnly.cshtml.cs. V takovém případě byste přidali
[Authorize(Policy = "Admin")]
jako atribut doAdminsOnlyModel
třídy . VýhodouAuthorizePage
výše uvedeného přístupu je, že zabezpečená stránka Razor Page nevyžaduje žádné úpravy. Aspekt autorizace se místo toho spravuje v souboru Program.cs.
Do souboru Pages/Shared/_Layout.cshtml zapracujte následující změny:
<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>
Předchozí změna podmíněně skryje odkaz Správa v záhlaví, pokud uživatel není správcem.
IsAdmin
Přidání deklarace identity k uživateli
Aby bylo možné určit, kteří uživatelé by měli získat IsAdmin=True
deklaraci identity, bude vaše aplikace při identifikaci správce spoléhat na potvrzenou e-mailovou adresu.
V souboru appsettings.json přidejte zvýrazněnou vlastnost:
{ "AdminEmail" : "admin@contosopizza.com", "Logging": {
Jedná se o potvrzenou e-mailovou adresu, která získá přiřazenou deklaraci identity.
V části Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs proveďte následující změny:
Začleňte následující zvýrazněný kód:
public class ConfirmEmailModel : PageModel { private readonly UserManager<RazorPagesPizzaUser> _userManager; private readonly IConfiguration Configuration; public ConfirmEmailModel(UserManager<RazorPagesPizzaUser> userManager, IConfiguration configuration) { _userManager = userManager; Configuration = configuration; }
Předchozí změna upraví konstruktor tak, aby přijímal
IConfiguration
z kontejneru IoC . ObsahujeIConfiguration
hodnoty z souboru appsettings.json a je přiřazený vlastnosti jen pro čtení s názvemConfiguration
.Do metody
OnGetAsync
začleňte zvýrazněné změny: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(); }
V předchozím kódu:
- Řetězec
AdminEmail
se načte zConfiguration
vlastnosti a přiřadí se kadminEmail
. - Pokud v souboru appsettings.json neexistuje odpovídající hodnota, použije se operátor
??
nulového sjednocení, který zajistí, žeadminEmail
je nastavený nastring.Empty
hodnotu . - Pokud se e-mail uživatele úspěšně potvrdí:
- Adresa uživatele je porovnána s
adminEmail
.string.Compare()
se používá pro porovnání bez rozlišování malých a velkých písmen. - Metoda
AddClaimAsync
třídyUserManager
je vyvolána za účelem uložení deklarace identityIsAdmin
do tabulkyAspNetUserClaims
.
- Adresa uživatele je porovnána s
- Řetězec
Na začátek souboru přidejte následující kód. Přeloží odkazy na
Claim
třídy vOnGetAsync
metodě :using System.Security.Claims;
Otestování deklarace identity správce
Pojďme provést poslední test, abychom ověřili nové funkce správce.
Ujistěte se, že jste uložili všechny změny.
Spusťte aplikaci pomocí
dotnet run
příkazu .Pokud ještě nejste přihlášení, přejděte do aplikace a přihlaste se pod stávajícím uživatelem. V záhlaví vyberte Seznam pizzy . Všimněte si, že uživateli se nezobrazují prvky uživatelského rozhraní pro odstranění nebo vytvoření pizzy.
V záhlaví není žádný odkaz Správci . Na panelu Adresa prohlížeče přejděte přímo na stránku AdminsOnly . Nahraďte
/Pizza
v adrese URL textem/AdminsOnly
.Tomuto uživateli je zakázán přechod na tuto stránku. Zobrazí se zpráva o odepření přístupu.
Vyberte možnost odhlášení.
Zaregistrujte nového uživatele s adresou
admin@contosopizza.com
.Stejně jako předtím potvrďte e-mailovou adresu nového uživatele a přihlaste se.
Jakmile se přihlásíte jako nový správce, vyberte v záhlaví odkaz Seznam pizzy .
Správce může vytvářet a odstraňovat pizzy.
V záhlaví vyberte odkaz Správci .
Zobrazí se stránka AdminsOnly .
Prohlídka tabulky AspNetUserClaims
Pomocí rozšíření SQL Server v editoru VS Code spusťte následující dotaz:
SELECT u.Email, c.ClaimType, c.ClaimValue
FROM dbo.AspNetUserClaims AS c
INNER JOIN dbo.AspNetUsers AS u
ON c.UserId = u.Id
Zobrazí se karta s výsledky podobnými následujícímu:
Typ deklarace identity | Deklarace identity | |
---|---|---|
admin@contosopizza.com | IsAdmin | Ano |
Deklarace identity IsAdmin
je uložená v tabulce AspNetUserClaims
jako dvojice klíč-hodnota. Záznam AspNetUserClaims
je přidružený k záznamu uživatele v tabulce AspNetUsers
.
Souhrn
V této lekci jste aplikaci upravili tak, aby ukládala deklarace identity a použila zásady podmíněného přístupu.