اخبار، مطالب و رویدادهای مرتبط با توسعه نرم افزار رادکام

ایمن سازی برنامه ها با استفاده از چارچوب نرم افزاری Identity - قسمت هشتم(پایانی) - مقدمه ای بر احراز مجوز مبتنی بر policy

حال می خواهیم کاربرانی را که قادر هستند نمرات دانش آموزان را وارد کنند تنها به اعضای دانشکده محدود کنیم. ASP.NET Core  مجوز دسترسی های مبتنی بر Claim را با یک مدل مبتنی بر Policy(سیاست) (policy-based model) مدیریت می کند. یک policy یک یا چند پیش نیاز و مقررات دارد که باید رعایت شود. این که عرض کردیم می تواند صرفا فقط وجود یک Claim برای یک Identity یا بررسی وجود مقادیر خاص یک Claim باشد.
ما یک خط مشی مجوز(authorization policy)  جدید را در متد ConfigureServices در کلاس startup ثبت می کنیم. در داخل متد یک فراخوانی به متد AddAuthorization  از آبجکت  services اضافه می کنیم. سپس باید گزینه های مربوط به تنظیمات مورد نظر خود را برای مجوز تعریف کنیم. با متد  AddPolicy ما یک Policy اضافه می کنیم و نام آن را FacultyOnly می گذاریم. سپس این Policy به یک Claim نیاز دارد، بنابراین ما یک فراخوانی به متد  RequireClaim اضافه می کنیم و نام claim ما  FacultyNumber خواهد بود. و برای فراخوانی این متد از Fluent API استفاده می کنیم.

services.AddAuthorization(options =>
{
   options.AddPolicy("FacultyOnly", policy => policy.RequireClaim("FacultyNumber"));
   });
 }

اکنون که   policy با نام FacultyOnly را تعریف کرده‌ایم، می‌توانیم آن را به ویژگی Authorize اضافه کنیم. حال کنترلر StudentController را باز کنیم و متد(اکشن) AddGrade را پیدا می کنیم.
در اینجا، برای متدی که verb آن از نوع HttpGet است، من ویژگی Authorize را اضافه می‌کنم و policy را مشخص می‌کنم، و نام policy همان نامی است که لحظاتی پیش ثبت کردیم.

[HttpGet]
[Authorize(Policy = "FacultyOnly")]
public IActionResult AddGrade()
{
return View();
}

ما همین کار را برای متدی که verb آن از نوع  HttpPost است نیز انجام می دهیم.

[HttpPost]
[Authorize(Policy = "FacultyOnly")]
public IActionResult AddGrade(CourseGrade model)
{

حالا که این تغییرات را انجام دادیم، بیایید دوباره به عنوان دانشجو وارد سیستم شویم. اکنون، در صفحه اصلی پورتال دانشجویی، سعی می کنم یک نمره اضافه کنم. این بار، من به طور خودکار به صفحه "دسترسی ممنوع " هدایت می شوم. از آنجایی که کاربر من claim(ادعای) FacultyOnly را ندارد، الزامات مربوط به policy برآورده نشده است. برای افزودن فیلد شماره دانشکده، باید فرآیند ثبت نام را به روز کنیم. که از کاربران دانشکده انتظار می رود آن را پر کنند.
RegisterViewModel  را  از داخل پوشه Models باز می کنیم.
ما در اینجا یک ویژگی جدید برای شماره دانشکده اضافه خواهیم کرد. این یک ویژگی که از نوع string است، به نام FacultyNumber خواهد بود.
و ما یک مشخصه به نام Display اضافه می کنیم تا در ویو به صورت عبارت Faculty Number نمایش داده شود.

using System.ComponentModel.DataAnnotations;

namespace Tutorial.AspNetSecurity.RouxAcademy.Models.AccountViewModels
{
public class RegisterViewModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }

[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }

[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }

[Display(Name = "Faculty Number")]
public string FacultyNumber { get; set; }
}
}

در مرحله بعد، باید ویوی ثبت نام را به روز کنیم تا شامل شماره دانشکده نیز شود. ویو ثبت نام را باز کنید و بیایید یک مکان خوب برای اضافه کردن این فیلد ورودی جدید پیدا کنیم. می توانیم آن را درست در زیر ConfirmPassword اضافه کنیم. ما یک div مشابه سایر کنترل‌های فرم ایجاد می‌کنیم تا ساختار UI ثابت بماند. بعد، من Label مرتبط را اضافه می کنم.
مشخص می کنیم که برچسب مربوط به فیلد متنی ما FacultyNumber خواهد بود، و ما یک استایل نیز اضافه خواهیم کرد. بعد، ما یک div هم برای فیلد متنی FacultyNumber اضافه می کنیم. نیز خود فیلد متنی را اضافه می کنیم که نوع ورودی متنی است. این فیلد برای FacultyNumber می باشد. و ما برای استایل از کلاس  form-control استفاده خواهیم کرد. آخرین چیزی که اضافه خواهیم کرد، بخش اعتبار سنجی ما است تا در صورت وجود هرگونه خطای تأیید اعتبار برای این کنترل جدید بتوانیم خطای مناسبی نمایش دهیم. و کلاس text-danger به آن می دهیم تا خطاها در متن قرمز ظاهر شوند.

@addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers"
@model Tutorial.AspNetSecurity.RouxAcademy.Models.AccountViewModels.RegisterViewModel
@{
ViewData["Title"] = "Register";
Layout = "_ContentLayout";
}

<fieldset class="account">
<h1>Register</h1>
<form asp-controller="Account" asp-action="Register" method="post" class="form-horizontal">
<h4>Create a new account.</h4>
<hr />
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Email" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input type="email" asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<label asp-for="Password" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input type="password" asp-for="Password" class="form-control" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<label asp-for="ConfirmPassword" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input type="password" asp-for="ConfirmPassword" class="form-control" />
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<label asp-for="FacultyNumber" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input type="text" asp-for="FacultyNumber" class="form-control" />
<span asp-validation-for="FacultyNumber" class="text-danger"></span>
</div>
</div>

<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<button type="submit" class="btn btn-default">Register</button>
</div>
</div>
</form>
</fieldset>

این کار هایی که انجام دادیم، تغییرات مورد نیاز ما در UI بود و در مرحله بعد به  سراغ AccountController می رویم. و بیایید متد ثبت نام را که از نوع  HttpPost هست، پیدا کنیم، و در اینجا نیز باید تغییراتی ایجاد کنیم. اولین کاری که ما انجام می دهیم این است که بررسی کنیم آیا FacultyNumber (شماره دانشکده) توسط کاربر در view وارد شده است یا خیر. بنابراین ما ویژگی FacultyNumber را در مدل بررسی می کنیم. و اگر مقدار وارد شده کاربر یک رشته خالی یا تهی نیست، کاری که باید انجام دهیم این است که یک Claim(ادعای) جدید به مجموعه Claim (ادعا)های کاربر اضافه کنیم. این یک آبجکت  IdentityUserClaim است که  ورودی از نوع string(رشته) می گیرد. این claim دارای یک  نوع ادعا (ClaimType) است که ما آن را برابر با همان مقدار FacultyNumber به عنوان یک string قرار می دهیم. و مقدار ClaimValue نیز برابر با مقداری است که کاربر برای FacultyNumber در ویو وارد کرده بود که در اصل همان  FacultyNumber گرفته شده از مدل خواهد بود.

 

// POST: /Account/Register
[HttpPost]
public async Task<IActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
IdentityUser user = new IdentityUser()
{
Email = model.Email,
UserName = model.Email
};

if (!string.IsNullOrEmpty(model.FacultyNumber))
{
user.Claims.Add(new IdentityUserClaim<string>
{
ClaimType = "FacultyNumber",
ClaimValue = model.FacultyNumber
});
}


var result = await _userManager.CreateAsync(user, model.Password);

if (result.Succeeded)
{
return RedirectToAction("Login", "Account");
}
else
{
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
}

// If we got this far, something failed, redisplay form
return View(model);
}

بیایید صفحه اصلی دانش آموز را نیز به روز کنیم، تا فهرستی از نمرات دانش آموز فعلی را نشان دهد. کنترلر StudentController را باز می کنیم و متد Index را پیدا می کنیم.
اولین کاری که انجام می دهم، "نام کاربری" کاربری که در حال حاضر وارد شده است را دریافت می کنیم. سپس از آن برای جستجوی نمراتی که به آن کاربر اختصاص داده شده است استفاده خواهیم کرد. در StudentDataContext، ما یک DbSet از نوع Grades  داریم و درون آن نمرات‌ی را جستجو می‌کنیم که نام کاربری مشابه کاربر فعلی دارند. و ما لیستی از نمرات را برمی گردانیم. مرحله آخر این است که این مجموعه را به view خود برگردانیم.

// GET: /<controller>/
public IActionResult Index()
{
var username = User.Identity.Name;

//Get grades for logged in student
var grades = _db.Grades.Where(g => g.StudentUsername == username).ToList();

return View(grades);
}

قبل از اینکه بتوانیم ادامه دهیم یک مرحله دیگر باقی مانده است. ما باید دستورهای migration پایگاه داده را برای StudentDataContext اجرا کنیم.  با این کار جدولی برای ذخیره نمرات دانش آموزان ایجاد می شود.
در منوی Tools و سپس زیر منوی NuGet Package Manager و سرانجام Package Manager Console را انتخاب کنید. و در اینجا، دستورات لازم را وارد کرده و اجرا می کنیم. اولین مورد اضافه کردن migration است. و ما باید نام dbcontext را مشخص کنیم که StudentDataContext است. بنابراین دستور زیر را اجرا می کنیم و مطمئن شویم که پوشه Migrations به پروژه ما اضافه شده است.

Add-Migration Init -context StudentDataContext 

و می توانید ببینید که دقیقاً در پوشه Migrations اضافه شده است. دستور بعدی که اجرا می کنیم دستور به روز رسانی پایگاه داده است. و دوباره، ما باید نام StudentDataContext خود را مشخص کنیم. دستور زیر را اجرا می کنیم

Update-Database  -context StudentDataContext

اکنون که  اعمال تغییرات کامل شد، می‌توانیم تغییراتی را که ایجاد کرده‌ایم آزمایش کنیم. بیایید جلوتر رفته و و برنامه را اجرا کنیم. روی لینک پورتال دانشجویی کلیک کنید و سپس روی لینک "Register as a new user / ثبت نام به عنوان کاربر جدید" کلیک کنید تا به عنوان یک کاربر جدید ثبت نام کنید. توجه داشته باشید که فیلد متنی FacultyNumber(شماره دانشکده) اکنون در فرم ثبت نام قرار دارد. ادامه می دهیم و یک کاربر دانشکده ثبت نام می کنیم. و نام کاربری را faculty@rouxacademy.com انتخاب می کنیم. من از همان رمز عبوری استفاده می کنیم که برای کاربر قبلی استفاده کردیم، که  حداقل الزامات قوی بودن رمز عبور را برآورده می کرد.
و برای شماره دانشکده، شماره FN123 را وارد کنیم. حالا به عنوان کاربر جدید دانشکده وارد می شویم و سپس روی پیوند Add Grade کلیک می کنیم. و اکنون، به عنوان یکی از اعضای دانشکده ، می‌توانیم به صفحه Add a Grade دسترسی داشته باشیم. من به دلیل داشتن Claim شماره دانشکده خود، حداقل الزامات مورد نیاز مربوط به Policy را برآورده کردم.
این مثال ساده به شما نشان می‌دهد که چگونه تعیین مجوز مبتنی بر policy، روشی انعطاف‌پذیر و قدرتمند برای تنظیم سطوح دسترسی است.

پست های مرتبط