Alıştırma - İlke tabanlı yetkilendirme ile talepleri kullanma
Önceki ünitede kimlik doğrulaması ile yetkilendirme arasındaki farkı öğrendiniz. Ayrıca, taleplerin yetkilendirme için ilkeler tarafından nasıl kullanıldığını da öğrendiniz. Bu ünitede, talepleri depolamak ve koşullu erişim için ilkeler uygulamak için Kimlik'i kullanacaksınız.
Pizza listesinin güvenliğini sağlama
Pizza Listesi sayfasının yalnızca kimliği doğrulanmış kullanıcılar tarafından görülebilecek yeni bir gereksinim aldınız. Ayrıca, yalnızca yöneticilerin pizza oluşturmasına ve silmesine izin verilir. Hadi kilitleyelim.
Pages/Pizza.cshtml.cs içinde aşağıdaki değişiklikleri uygulayın:
sınıfına
PizzaModel
bir[Authorize]
öznitelik ekleyin.[Authorize] public class PizzaModel : PageModel
özniteliği, sayfa için kullanıcı yetkilendirme gereksinimlerini açıklar. Bu durumda, kullanıcının kimliğinin doğrulanması dışında başka bir gereksinim yoktur. Anonim kullanıcıların sayfayı görüntülemesine izin verilmez ve oturum açma sayfasına yönlendirilir.
Dosyasının üst kısmındaki yönergelerine
Authorize
using
aşağıdaki satırı ekleyerek başvuruyu çözümleyin:using Microsoft.AspNetCore.Authorization;
PizzaModel
sınıfına aşağıdaki özelliği ekleyin:[Authorize] public class PizzaModel : PageModel { public bool IsAdmin => HttpContext.User.HasClaim("IsAdmin", bool.TrueString); public List<Pizza> pizzas = new();
Yukarıdaki kod, kimliği doğrulanmış kullanıcının
True
değerine sahip birIsAdmin
talebinin olup olmadığını belirler. Bu değerlendirmenin sonuçlarına,IsAdmin
adlı bir salt okunur özellik aracılığıyla erişilir.hem ve
OnPost
OnPostDelete
yöntemlerinin başına ekleyinif (!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"); }
Bir sonraki adımda yönetici olmayanlar için oluşturma/silme kullanıcı arabirimi öğelerini gizleyeceksiniz. Bu, HttpRepl veya Postman gibi bir araçla saldırganların bu uç noktalara doğrudan erişmesini engellemez. Bu denetimin eklenmesi, denenirse http 403 durum kodunun döndürülmesini sağlar.
Pages/Pizza.cshtml'de yönetici kullanıcı arabirimi öğelerini yönetici olmayanlardan gizlemek için denetimler ekleyin:
Yeni pizza formunu gizle
<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> }
Pizza sil düğmesini gizle
<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>
Önceki değişiklikler, yalnızca yöneticiler tarafından erişilebilir olması gereken kullanıcı arabirimi öğelerinin yalnızca kimliği doğrulanmış kullanıcı yönetici olduğunda işlenmesine neden olur.
Yetkilendirme ilkesi uygulama
Kilitlemeniz gereken bir şey daha var. Sayfalar/AdminsOnly.cshtml adlı, yalnızca yöneticiler tarafından erişilebilir olması gereken bir sayfa vardır. Talebi denetlemek IsAdmin=True
için bir ilke oluşturalım.
Program.cs dosyasında aşağıdaki değişiklikleri yapın:
Aşağıdaki vurgulanmış kodu ekleyin:
// 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();
Yukarıdaki kod,
Admin
adlı bir yetkilendirme ilkesi tanımlar. İlke, kullanıcının kimliğinin doğrulanmasını veTrue
olarak ayarlı birIsAdmin
talebinin olmasını gerektirir.çağrısında
AddRazorPages
aşağıdaki gibi değişiklik yapın:builder.Services.AddRazorPages(options => options.Conventions.AuthorizePage("/AdminsOnly", "Admin"));
yöntem
AuthorizePage
çağrısı, ilkeyi uygulayarak /AdminsOnly Razor Page yolununAdmin
güvenliğini sağlar. İlke gereksinimlerini karşılamayan kimliği doğrulanmış kullanıcılara bir Erişim reddedildi iletisi gösterilir.İpucu
Alternatif olarak, bunun yerine AdminsOnly.cshtml.cs dosyasını değiştirmiş de olabilirdiniz. Bu durumda, sınıfına
AdminsOnlyModel
öznitelik olarak eklemeniz[Authorize(Policy = "Admin")]
gerekir. Yukarıda gösterilen yaklaşımınAuthorizePage
bir avantajı, güvenliği sağlanan Razor Sayfasının hiçbir değişiklik gerektirmemiş olmasıdır. Bunun yerine yetkilendirme yönü Program.cs'de yönetilir.
Pages/Shared/_Layout.cshtml dosyasında aşağıdaki değişiklikleri ekleyin:
<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>
Yukarıdaki değişiklik, kullanıcı yönetici değilse üst bilgideki Yönetici bağlantısını koşullu olarak gizler.
IsAdmin
Talebi kullanıcıya ekleme
Hangi kullanıcıların talebi alması gerektiğini belirlemek için uygulamanız, yöneticiyi IsAdmin=True
tanımlamak için onaylanmış bir e-posta adresini kullanır.
appsettings.json dosyasına vurgulanan özelliği ekleyin:
{ "AdminEmail" : "admin@contosopizza.com", "Logging": {
Bu, talebin atandığı onaylanan e-posta adresidir.
Alanlar/Kimlik/Sayfalar/Hesap/ConfirmEmail.cshtml.cs içinde aşağıdaki değişiklikleri yapın:
Aşağıdaki vurgulanmış kodu ekleyin:
public class ConfirmEmailModel : PageModel { private readonly UserManager<RazorPagesPizzaUser> _userManager; private readonly IConfiguration Configuration; public ConfirmEmailModel(UserManager<RazorPagesPizzaUser> userManager, IConfiguration configuration) { _userManager = userManager; Configuration = configuration; }
Yukarıdaki değişiklik, oluşturucuyu IoC kapsayıcısından bir
IConfiguration
alacak şekilde değiştirir.IConfiguration
appsettings.json dosyasındaki değerleri içerir ve adlıConfiguration
salt okunur özelliğe atanır.Vurgulanan değişiklikleri
OnGetAsync
metoduna uygulayın: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(); }
Yukarıdaki kodda:
- Dize
AdminEmail
özelliğindenConfiguration
okunur ve öğesineadminEmail
atanır. - null birleşim işleci
??
, appsettings.json dosyasında karşılık gelen değer yoksa değerinin olarak ayarlandığındanstring.Empty
emin olmakadminEmail
için kullanılır. - Kullanıcının e-postası başarıyla onaylanırsa:
- Kullanıcının adresi ile
adminEmail
karşılaştırılır.string.Compare()
büyük/küçük harfe duyarsız karşılaştırma için kullanılır. AspNetUserClaims
tablosundakiIsAdmin
talebini kaydetmek içinUserManager
sınıfınınAddClaimAsync
metodu çağrılır.
- Kullanıcının adresi ile
- Dize
Aşağıdaki kodu dosyanın en üstüne ekleyin. Yöntemindeki
Claim
sınıf başvurularınıOnGetAsync
çözümler:using System.Security.Claims;
Yönetici talebini test etme
Yeni yönetici işlevselliğini doğrulamak için son bir test yapalım.
Tüm değişikliklerinizi kaydettiğinizden emin olun.
uygulamayı ile
dotnet run
çalıştırın.Henüz oturum açmadıysanız uygulamanıza gidin ve mevcut bir kullanıcıyla oturum açın. Üst bilgiden Pizza Listesi'ni seçin. Kullanıcıya pizzaları silmek veya oluşturmak için kullanıcı arabirimi öğelerinin sunulmadığından dikkat edin.
Üst bilgide Yöneticiler bağlantısı yoktur. Tarayıcının adres çubuğunda doğrudan AdminsOnly sayfasına gidin. URL'deki değerini ile
/AdminsOnly
değiştirin/Pizza
.Kullanıcının bu sayfaya gitmesi yasaktır. Erişim engellendi iletisi görüntülenir.
Oturumu Kapat’ı seçin.
adresine
admin@contosopizza.com
yeni bir kullanıcı kaydedin.Daha önce olduğu gibi, yeni kullanıcının e-posta adresini onaylayın ve oturum açın.
Yeni yönetici kullanıcıyla oturum açtıktan sonra üst bilgideki Pizza Listesi bağlantısını seçin.
Yönetici kullanıcı pizza oluşturabilir ve silebilir.
Üst bilgide Yöneticiler bağlantısını seçin.
AdminsOnly sayfası görüntülenir.
AspNetUserClaims tablosunu inceleme
VS Code'da SQL Server uzantısını kullanarak aşağıdaki sorguyu çalıştırın:
SELECT u.Email, c.ClaimType, c.ClaimValue
FROM dbo.AspNetUserClaims AS c
INNER JOIN dbo.AspNetUsers AS u
ON c.UserId = u.Id
Aşağıdakine benzer sonuçlar içeren bir sekme görüntülenir:
E-posta | Claimtype | ClaimValue |
---|---|---|
admin@contosopizza.com | IsAdmin | Doğru |
IsAdmin
talebi, AspNetUserClaims
tablosunda anahtar-değer çifti olarak depolanır. AspNetUserClaims
kaydı, AspNetUsers
tablosundaki kullanıcı kaydıyla ilişkilendirilir.
Özet
Bu ünitede, uygulamayı talepleri depolayıp koşullu erişim için ilkeler uygulayacak şekilde değiştirdiniz.