شروع دوره پایتون از پنجشنبه 1 خرداد ، مقدماتی تا پیشرفته، بدون پیش نیاز شروع دوره پایتون از پنجشنبه 1 خرداد ، مقدماتی تا پیشرفته، بدون پیش نیاز
🎯 ثبت نام
بستن تبلیغات
تسلط کامل بر سی‌شارپ با یک دوره پروژه‌محور

یادگیری سی شارپ از مفاهیم پایه تا پروژه محور: شی‌گرایی، کار با SQL و LINQ، ORMها (Entity Framework)، ساخت پروژه مدیریت رستوران با گزارشات حرفه‌ای و امکانات کامل!

مشاهده بیشتر
تسلط جامع بر MVC Core برای توسعه وب حرفه‌ای

یادگیری MVC Core از مبانی تا پیشرفته: شی‌گرایی، Routing، Entity Framework، امنیت، تست یونیت، Razor، Ajax، و پروژه‌های کاربردی! یک دوره کامل برای تسلط بر توسعه وب با ASP.NET Core. به صورت حضوری و آنلاین!

مشاهده بیشتر

نحوه ایجاد و حذف کردن Role ها

ایجاد و حذف کردن Role ها

حالا که نرم افزار برای کار با Role ها آماده شده است، می خواهم ابزاری مدیریتی ایجاد کنم تا آن ها را مدیریت کند. کارم را با اصول اولیه و تعریف کردن action method ها و view هایی که Role ها به کمک آن ها می توانند ایجاد و حذف شوند، آغاز می کنم. Controller ای به نام RoleAdmin را به پروژه اضافه کرده ام. این Controller را می توانید در قطعه کد زیر مشاهده کنید.

Using System.ComponentModel. DataAnnotations;
Using System.Linq;
Using System.Threading. Tasks;
Using System.Web;
using System.Web.Mvc;
using Microsoft. AspNet. Identity;
using Microsoft. AspNet. Identity.Owin;
using Users.Infrastructure;
using Users.Models;
namespace Users.Controllers {
public class RoleAdminController : Controller {
public ActionResult
Index() { 
Return View(RoleManager.Roles);
}
Public ActionResult Create() {
return View();
}
	[HttpPost]
public async Task< ActionResult >
Create([Required]string name) {
if (ModelState.IsValid) {
IdentityResult result = await
RoleManager.CreateAsync(new AppRole(name));
if (result.Succeeded) {
	return RedirectToAction("Index");
	} else {
	AddErrorsFromResult(result);
	}
}
return View(name);
}
[HttpPost]
public async Task< ActionResult >
Delete(string id) { AppRole role = await
RoleManager.FindByIdAsync(id); if (role!= null) {
IdentityResult result = await
RoleManager.DeleteAsync(role);
if (result.Succeeded) {
return RedirectToAction("Index");
} else {
return View("Error", result.Errors);
		}
	} else {
	return View("Error", new string[] { "Role Not Found" });
}
	}
AddErrorsFromResult(IdentityResult result) {
 foreach (string error in result.Errors) {
ModelState.AddModelError("", error);
}
}
private AppUserManager UserManager { get {
return HttpContext.GetOwinContext().GetUserManager< AppUserManager >();
}
}
Private AppRoleManager RoleManager { 
get {
return HttpContext.GetOwinContext().GetUserManager< AppRoleManager >();
}
}
}

بسیاری از تکنیک هایی که در اینجا استفاده شده اند همان هایی هستند که در فصل 13 در Admincontroller استفاده کردم. مثل مشخصه ی UserManager که نمونه ای از کلاس AppUserManager را دریافت می کند و متد AddErrorsFromResult که خطاهای گزارش شده در شیء IdentityResult را پردازش می کند و آن ها را به model state اضافه می کند.
همچنین مشخصه ای به نام RoleManager را تعریف کرده ام که کار آن دریافت نمونه ای از کلاس AppRoleManager است. من برای دریافت و دستکاری Role ها در برنامه از این مشخصه در action method ها استفاده کرده ام. با توجه به اینکه این action method ها از همان الگویی پیروی می کنند که در فصل 13 از آن استفاده کردم، نیازی به توضیح مفصل آن ها نیست. استفاده از کلاس AppRoleManager به جای AppUserManager و فراخوانی متدها را در جدول 8-14 توضیح داده ام.

ایجاد view

View های کنترلرRoleAdmin، markup استاندارد Razor و HTML هستند، اما چون می خواهم این view ها را مجددا ایجاد کنید آن ها را در این فصل آورده ام. میخواهم اسامی کاربرانی که عضو هریک از Role ها هستند را نمایش دهم. کلاس Entity Framework IdentityRole مشخصه ی Users را تعریف می کند که این مشخصه مجموعه ای از اشیاء کاربری IdentityUserRole را برگشت میدهد. این اشیاء بیانگر اعضای Role هستند. هریک از اشیاء IdentityUserRole دارای مشخصه ی UserId هستند که ID منحصر به فرد کاربر را برگشت می دهند. می خواهم نام کاربری هریک از ID ها را دریافت کنم. فایل کلاسی به نام IdentityHelpers.cs را به پوشه ی Infrastructure اضافه کرده ام و از آن برای تعریف کلاس نشان داده شده در قطعه کد زیر استفاده کرده ام.

Using System.Web;
Using System.Web. Mvc;
using Microsoft.AspNet.Identity.Owin;
namespace Users.Infrastructure {
public static class IdentityHelpers {
public static MvcHtmlString GetUserName(this HtmlHelper html, string id) { AppUserManager mgr=HttpContext.Current.GetOwinContext().GetUserManager< AppUserManager >(); 
return new MvcHtmlString(mgr.FindByIdAsync(id).Result.UserName);
}
}
}

متدهای کمکی و اختصاصی HTML به عنوان افزونه های کلاس HtmlHelper تعریف می شوند. متد کمکی من که اسم آن را GetUsername گذاشته ام، آرگومانی رشته ای متشکل از ID کاربر را می گیرد، از طریق متد GetOwinContext.GetUserManager نمونه ای از AppUserManager را دریافت می کند (GetOwinContext متد گسترشی کلاس HttpContext است)، و برای شناسایی موقعیت نمونه ی AppUser مربوط به ID همچنین برای برگشت دادن مقدار مشخصه ی UserName از متد FindByIdAsync استفاده می کند.
قطعه کد زیر محتوای داخل فایل Index.cshtml موجود در پوشه ی Views/RoleAdmin را نشان می دهد. این فایل را با راست کلیک کردن بر روی Indexaction method موجود در code editor و انتخاب Add View از منوی باز شده ایجاد کرده ام.

@using Users.Models
@using Users.Infrastructure
@model IEnumerable< AppRole >
@{ ViewBag.Title = "Roles"; }
< div class="panel panel-primary" >
< div class="panel-heading" >Roles< /div >
< table class="table table-striped" >
< tr >
< th >ID< /th >
< th >Name< /th >
< th >Users< /th >
< th >< /th >
< /tr >
@if (Model.Count() == 0) {
< tr >< td colspan="4" class="text-center" >No Roles< /td >< /tr >
} else {
foreach (AppRole role in Model) {
< tr >
< td >@role.Id< /td >
< td >@role.Name< /td >
< td >
@if (role.Users == null || role.Users.Count == 0) {
@: No Users in Role
} else {
< p >
@string.Join(", ",role.Users.Select(x= >Html.GetUserName(x.UserId)))
< /p >
}
< /td >
< td >
@using (Html.BeginForm("Delete", "RoleAdmin", new { id = role.Id })) {
@Html.ActionLink("Edit", "Edit", new { id = role.Id }, new { @class = "btn btn-primary btn-xs" })
< button class="btn btn-danger btn-xs" type="submit" >
Delete
< /button >
}
< /td >
< /tr >
}
}
< /table >
< /div >
@Html.ActionLink("Create", "Create", null, new { @class = "btn btn-primary" })

این view لیستی از role های تعریف شده توسط نرم افزار به همراه کاربرانی که عضو این نقش ها هستند را نمایش می دهد. برای دریافت اسم هریک از کاربران از متد کمکی GetUserName استفاده می کنم.
در قطعه کد زیر فایل Views/RoleAdmin/Create.cshtml نشان داده شده است. با کمک این فایل می توانم role های جدید را ایجاد کنم.

@model string
@{ ViewBag.Title = "Create Role";}
< h2 >Create Role< /h2 >
@Html.ValidationSummary(false)
@using (Html.BeginForm()) {
< div class="form-group" >
< label >Name< /label >
< input name="name" value="@Model" class="form-control" / >
< /div >
< button type="submit" class="btn btn-primary" >Create< /button >
@Html.ActionLink("Cancel", "Index", null, new { @class = "btn btn-default" })
}

تنها اطلاعاتی که برای ایجاد یک view جدید نیاز است، name است، که آن را با استفاده از المان استاندارد input و دادن مقدار آن به Createaction method گردآوری می کنم.

امتحان کردن فرآیند ایجاد و حذف Role ها

برای آنکه این controller جدید را امتحان کنید، برنامه را باز کنید و به آدرس /RoleAdmin/Index بروید. برای اینکه role جدیدی را ایجاد کنید، بر روی دکمه Create کلیک کنید، اسمی را در المان input وارد کنید و بر روی دومین دکمه Create کلیک کنید. view جدید در دیتابیس ذخیره شده و زمانی که مرورگر به Index action هدایت شود، مانند شکل زیر نمایش داده می شود. برای حذف کردن role ایجاد شده از برنامه بر روی دکمه Delete کلیک کنید.


دوره آموزش MVC 5

مدیریت Role membership ها

برای اختیاردهی به کاربران تنها ایجاد و حذف کردن نقش ها کفایت نمی کند، بلکه باید بتوانم role membership ها را نیز مدیریت کنم به این صورت که کاربران را به role هایی که برنامه آن ها را تعریف می کند اختصاص دهم و آن ها را حذف کنم. این فرایند ساده است، اما طی آن باید داده های role را از کلاس AppRoleManager دریافت کرد و متدهای تعریف شده توسط کلاس AppUserManager که کاربران را به role ها مرتبط می کند را فراخوانی کرد.
این کار را با تعریف کردن view model هایی شروع کردم که بتوانم با کمک آن ها عضویت یک role را ارائه کنم و مجموعه ی جدیدی از دستورالعمل های مربوط به عضویت را از کاربر دریافت کنم. در قطعه کد زیر مواردی که به فایل UserViewModels.cs اضافه کرده ام نشان داده شده است.

using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
namespace Users.Models { 
public class CreateModel {
[Required]
public string Name { get; set; }
[Required]
public string Email { get; set; }
[Required]
public string Password { get; set; }
}
public class LoginModel {
[Required]
public string Name{get;set;}
[Required]
public string Password { get; set; }
}
public class RoleEditModel {
public AppRole Role { get; set; }
public IEnumerable< AppUse r> Members { get; set; } public IEnumerable< AppUser > NonMembers { get; set; }
}
public class RoleModificationModel {
[Required]
public string RoleName { get; set; }
public string[] IdsToAdd { get; set; }
public string[] IdsToDelete { get; set; }
}
}

کلاس RoleEditModel این امکان را فراهم می کند تا جزئیات role ها و کاربران را در سیستمی که توسط عضویت دسته بندی شده است عبور دهم. برای آن که بتوانم اسم و ID هریک از کاربران در view ای که عضویت ها می توانند به کمک آن ویرایش شوند را استخراج کنم، از اشیاء AppUser در view model استفاده می کنم. کلاس RoleModificationModel را بعد از اعمال تغییرات کاربر از سیستم model binding دریافت خواهم کرد. این کلاس به جای اشیاء AppUser شامل ID کاربران است و این دقیقا همان چیزی است که برای تغییر role membership ها به آن نیاز دارم.
حالا که view model ها تعریف شدند، می توانم action method ها را به کنترلری اضافه کنم که role membership ها بتوانند به کمک آن تعریف شوند. قطعه کد زیر تغییراتی که به RoleAdmincontroller اعمال کرده ام را نشان می دهد.

Using System.ComponentModel.DataAnnotations;
Using System.Linq;
Using System.Threading.Tasks;
Using System.Web;
Using System.Web.Mvc;
Using Microsoft.AspNet.Identity;
Using Microsoft.AspNet.Identity.Owin;
Using Users.Infrastructure;
Using Users.Models;
Using System.Collections.Generic;
namespace Users.Controllers {
public class RoleAdminController : Controller {
// ...اکشن متدهای دیگر برای مختصر کردن کار حذف شده اند...
public async Task< ActionResult > Edit(string id)
{
AppRole role = await RoleManager.FindByIdAsync(id);
string[] memberIDs = role.Users.Select(x = > x.UserId).ToArray(); IEnumerable< AppUser > members= UserManager.Users.Where(x = > memberIDs.Any(y = > y == x.Id));
IEnumerable< AppUser > nonMembers = UserManager.Users.Except(members); return View(new RoleEditModel {
Role = role, Members = members,
NonMembers = nonMembers
});
}
[HttpPost]
public async Task< ActionResult > Edit(RoleModificationModel model) { IdentityResult result;
if (ModelState.IsValid) {
foreach (string userId in model.IdsToAdd ?? new string[] { }) {
result = await UserManager.AddToRoleAsync(userId, model.RoleName); if (!result.Succeeded) {
return View("Error", result.Errors);
}
}
foreach (string userId in model.IdsToDelete ?? new string[] { }) { result = await UserManager.RemoveFromRoleAsync(userId,
model.RoleName);
if (!result.Succeeded) {
return View("Error", result.Errors);
}
}
return RedirectToAction("Index");
}
return View("Error", new string[] { "Role Not Found" });
}
private void AddErrorsFromResult(IdentityResult result) { foreach (string error in result.Errors) {
ModelState.AddModelError("", error);
}
}
private AppUserManager UserManager { get {
return HttpContext.GetOwinContext().GetUserManager< AppUserManager >();
}
}
private AppRoleManager RoleManager { get {
return HttpContext.GetOwinContext().GetUserManager< AppRoleManager >();
}
}
}
}

اغلب کدهایی که در نسخه ی GET مربوط به Editaction method وجود دارند، مسئول تولید مجموعه هایی از عضوها و غیر عضوهای role انتخابی هستند. این کار را با استفاده از LINQ انجام میدهد. بعد از گروه بندی کاربران من متد view را فراخوانی می کنم و نمونه ای جدید از کلاس RoleEditModel که در قطعه کد زیر تعریف کرده ام را عبور می دهم.
نسخه ی POST مربوط به متد Edit مسئول اضافه کردن کاربران به role ها و حذف کاربران از آن ها می باشد. کلاس AppUserManager تعدادی از متدهای مرتبط با role را از کلاس پایه ی خود که در جدول زیر به آن پرداخته ام به ارث می برد.

نام متد
توضیحات
AddToRoleAsync(id, name))
کاربری با ID مشخص شده را به role ای با اسم مشخص شده، اضافه می کنم.
GetRolesAsync(id)
لیستی از اسامی role هایی که در آن ها کاربری با ID مشخص شده عضو است را برگشت می دهد.
IsInRoleAsync(id, name)
اگر کاربری با ID مشخص شده عضوی از role ای با اسم مشخص شده باشد، true را برگشت می دهد.
RemoveFromRoleAsync(id, name)
کاربری با ID مشخص شده را به عنوان عضوی از role ای با اسم مشخص شده، حذف می کند.

موضوع عجیبی که در این متدها وجود دارد این است که متد های مرتبط به role بر روی اسامی role ها و ID های کاربران عملیات انجام می دهند. با وجود اینکه role ها نیز شناسه های منحصر به فردی دارند. دلیل این امر این است که کلاس ویو مدل RoleModificationModel من دارای مشخصه ی RoleName است. در قطعه کد زیر view مربوط به فایل Edit.cshtml نشان داده شده است. این فایل را به پوشه ی Views/RoleAdmin اضافه کرده ام و از آن برای تعریف markup ای استفاده کرده ام که کاربر بتواند role membership ها را ویرایش کند.

@using Users.Models
@model RoleEditModel
@{ ViewBag.Title = "Edit Role";}
@Html.ValidationSummary()
@using (Html.BeginForm()) {
< input type="hidden" name="roleName" value="@Model.Role.Name" / >
< div class="panel panel-primary" >
< div class="panel-heading" >Add To @Model.Role.Name< /div >
< table class="table table-striped" >
@if (Model.NonMembers.Count() == 0) {
< tr >< td colspan="2" >All Users Are Members< /td >< /tr >
} else {
< tr >< td >User ID< /td >< td >Add To Role< /td >< /tr > foreach (AppUser user in Model.NonMembers) {
< tr >
< td >@user.UserName< /td >
< td >
< input type="checkbox" name="IdsToAdd" value="@user.Id" >
< /td >
< /tr >
}
}
< /table >
< /div >
< div class="panel panel-primary" >
< div class="panel-heading" >Remove from @Model.Role.Name< /div >
< table class="table table-striped" >
@if (Model.Members.Count() == 0) {
< tr >< td colspan="2" >No Users Are Members< /td >< /tr >
} else {
< tr >< td >User ID< /td >< td >Remove From Role< /td >< /tr > foreach (AppUser user in Model.Members) {
< tr >
< td >@user.UserName< /td >
< td >
< input type="checkbox" name="IdsToDelete" value="@user.Id" >
< /td >
< /tr >
}
}
< /table >
< /div >
< button type="submit" class="btn btn-primary" >Save< /button >
@Html.ActionLink("Cancel", "Index", null, new { @class = "btn btn-default" })
}

این view شامل دو جدول است. یکی برای کاربرانی که عضو role انتخابی نیستند و دیگری برای کاربرانی که عضو هستند. هر یک از اسامی کاربران به همراه گزینه ای که امکان تغییر را برای membership فراهم می کند، نمایش داده می شوند.


امتحان کردن فرآیند Role Membership

اضافه کردن کلاس AppRoleManager به برنامه باعث می شود که Entity Framework محتوای داخل دیتابیس را پاک کرده و طرح (schema) را مجددا می سازد. این یعنی تمامی کاربرانی که شما در فصل قبل ایجاد کرده بودید پاک می شوند. برای آن که کاربرانی برای اختصاص دادن به role ها وجود داشته باشند، نرم افزار را باز کنید و به آدرس /Admin/Index بروید، و کاربران را به کمک جزئیات ارائه شده در جدول زیر ایجاد کنید.

نام
ایمیل
رمزعبور
Alice
alice@example.com
MySecret
Bob
bob@example.com
MySecret
Joe
joe@example.com
MySecret

نکته :

در برنامه های فرضی و تمرینی پاک کردن دیتابیس کاربران مشکلی ایجاد نمی کند اما این کار در برنامه های واقعی می تواند مشکل زا باشد. در فصل 15 چگونگی مدیریت اعمال تغییرات بر طرح دیتابیس را به شما خواهم آموخت.


برای آن که مدیریت role membership ها را امتحان کنید، به آدرس /RoleAdmin/Index رفته و role ای به نام Users به همراه دستورالعمل های موجود در بخش «امتحان کردن فرآیند ایجاد و حذف role ها» ایجاد کنید. بر روی دکمه ی Edit کلیک کنید و گزینه ها را به گونه ای انتخاب کنید که Alice و Joe عضو role باشند و Bob عضو نباشد.


دوره آموزش MVC 5
نکته :

اگر خطایی دریافت کنید که به شما بگوید که همین الان یک data reader آزاد وجود دارد، در این صورت تنظیمات MultipleActiveResultSets در رشته ی اتصالی که در فصل 13 گفته شد را بر روی true قرار نداده اید.


بر روی دکمه ی save کلیک کنید تا کنترلر role membership ها را آپدیت کند و مرورگر را به Indexaction هدایت کند. همانطور که در شکل زیر نشان داده شده است، خلاصه ی رول Users نشان می دهد که در حال حاضر Alice و Joe عضو هستند.


دوره آموزش MVC 5

استفاده از Role ها برای اختیاردهی به کاربر

حالا که توانایی مدیریت role ها دارم، می توانم از آن ها به عنوان اساس اختیاردهی از طریق Authorize attribute استفاده کنم. برای آنکه امتحان کردن اختیاردهی role محور را آسان تر کنم، متد Logout را به Accountcontroller اضافه کرده ام (همانطور که در قطعه کد زیر نشان داده شده است). این کار باعث می شود ورود و خروج به نرم افزار به عنوان یک کاربر متفاوت راحت تر شود، تا بتوان نتیجه ی role membership را مشاهده کرد.

Using System.Threading.Tasks; using System.Web.Mvc;
Using Users.Models;
Using Microsoft.Owin.Security;
Using System.Security.Claims; using Microsoft.AspNet.Identity;
Using Microsoft.AspNet.Identity.Owin; using Users.Infrastructure;
Using System.Web;
namespace Users.Controllers {
[Authorize]
public class AccountController : Controller {
[AllowAnonymous]
public ActionResult Login(string returnUrl) { ViewBag.returnUrl = returnUrl;
return View();
}
[HttpPost] [AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task< ActionResult > Login(LoginModel details, string returnUrl) {
// ...statements omitted for brevity...
}
[Authorize]
public ActionResult Logout() { AuthManager.SignOut();
return RedirectToAction("Index", "Home");
}
private IAuthenticationManager AuthManager { get {
return HttpContext.GetOwinContext().Authentication;
}
}

private AppUserManager UserManager {get{
return HttpContext.GetOwinContext().GetUserManager< AppUserManager >();
}
}
}
}

برای آنکه action method جدیدی را به view اضافه کنم و برخی از اطلاعات در رابطه با کاربر احراز هویت شده را به آن عبور دهم، همانطور که در قطعه کد زیر نشان داده شده است، Homecontroller را آپدیت کرده ام.

using System.Web.Mvc;
using System.Collections.Generic;
using System.Web;
using System.Security.Principal;

namespace Users.Controllers {

public class HomeController : Controller {

[Authorize]
public ActionResult Index() {
return View(GetData("Index"));
}

[Authorize(Roles="Users")]
public ActionResult OtherAction() {
return View("Index", GetData("OtherAction"));
}

private Dictionary< string, object > GetData(string actionName) { Dictionary< string, object > dict
= new Dictionary< string, object >();

dict.Add("Action", actionName);
dict.Add("User", HttpContext.User.Identity.Name); dict.Add("Authenticated", HttpContext.User.Identity.IsAuthenticated); dict.Add("Auth Type", HttpContext.User.Identity.AuthenticationType); dict.Add("In Users Role", HttpContext.User.IsInRole("Users"));
return dict;
}
}
}

تغییری در Authorizeattribute مربوط به Indexaction method ایجاد نکرده ام. اما در زمان اعمال این attribute در متد OtherAction مشخصه ی Roles را تنظیم کرده ام. این کار باعث می شود تنها اعضای Usersrole بتوانند به آن دسترسی پیدا کنند. متد GetData را نیز تعریف کرده ام. این متد برخی از اطلاعات پایه ای یا اولیه ی مربوط به هویت کاربر را با استفاده از مشخصات موجود، از طریق شیء HttpContext اضافه می کند. آخرین تغییر را بر روی فایل Index.cshtml موجود در پوشه ی Views/Home اعمال کرده ام. این فایل توسط هر دو اکشن موجود در Homecontroller استفاده می شود تا بتوان لینکی را اضافه کرد که متد Logout موجود در Accountcontroller را هدف قرار دهد.

@{ ViewBag.Title = "Index"; }

< div class="panel panel-primary" >
< div class="panel-heading" >User Details< /div >
< table class="table table-striped" >
@foreach (string key in Model.Keys) {
< tr >
< th >@key< /th >
< td >@Model[key]< /td >
< /tr >
}
< /table >
< /div >

@Html.ActionLink("Sign Out", "Logout", "Account", null, new {@class = "btn btn-primary"})


نکته :

برای اختیاردهی دسترسی بر اساس لیستی از نام های کاربری مجزا از Authorizeattribute نیز می توان استفاده کرد. این ویژگی مناسب پروژه های کوچک است. اما این یعنی در صورت مجموعه ای از کاربرانی که شما به آن ها اختیار داده اید تغییر کند، باید کد موجود در کنترلر هایتان را تغییر دهید. این شما را با چرخه ای از آزمون و استقرار (test and deploy) مواجه می کند. استفاده از role ها برای اختیار دهی به کاربران باعث مجزا شدن برنامه تان از تغییرات اعمال شده بر روی حساب های کاربری می شود و این امکان را به شما می دهد تا از طریق عضویت های ذخیره توسط توسط ASP.NET Identity ، دسترسی به برنامه را کنترل کنید.


برای آنکه احراز هویت را امتحان کنید، برنامه را باز کنید و به آدرس /Home/Index بروید. برای آنکه شما بتوانید نام کاربری و رمز عبور را وارد کنید، آدرس مرورگر تغییر می کند. مهم نیست که کدام یک از جزئیات کاربری موجود در جدول را برای احراز هویت انتخاب کنید، زیرا Authorizeattribute بر Indexaction اعمال شده است. به همین دلیل می توان به تمامی کاربران احراز هویت شده دسترسی داشت .
با این حال، اگر شما آدرس /Home/OtherAction را درخواست کنید، در این صورت جزئیات کاربری ای که از جدول انتخاب می کنید، متفاوت خواهند بود. زیرا فقط Alice و Joe اعضای Usersrole هستند. این role برای دسترسی به متد OtherAction الزامی است. اگر شما با اطلاعات کاربری Bob وارد برنامه شوید، در این صورت آدرس مرورگر به گونه ای تغییر خواهد کرد که بار دیگر از شما درخواست می شود تا نام کاربری و رمز عبور را وارد کنید .
تغییر مسیر کاربرانی که از قبل احراز هویت شده اند برای دریافت اطلاعات محرمانه ی بیشتر به ندرت پیش می آید که کار مفیدی باشد. برای بررسی این موضوع که آیا کاربر احراز هویت شده است یا خیر و در صورت احراز هویت، مسیر آن را به view مشترک Error تغییر دهم، Loginaction موجود در Accountcontroller را اصلاح کرده ام. در تیتر قطعه کد زیر این تغییرات نشان داده شده است.

Using System. Threading. Tasks;
Using System. Web. Mvc;
Using Users. Models;
Using Microsoft. Owin. Security;
Using System. Security. Claims;
Using Microsoft. AspNet. Identity;
Using Microsoft. AspNet. Identity. Owin;
Using Users. Infrastructure;
Using System. Web;
Namespace Users.Contr ollers {
	[Authore]
	public class AccountController : Controller {
[AllowAnonymous]
public ActionResult Login(string returnUrl) {
if (HttpContext.User.Identity.IsAuthenticated) {
return View("Error", new string[] { "Access Denied" });}
			ViewBag.returnUrl= returnUrl;
return View();
}
		[HttpPost]
		[AllowAnonymous]
		[ValidateAntiForgeryToken]
		public async Task< ActionResult > Login(LoginModel details, string returnUrl) {
// ...code omitted for brevity...
}

[Authorize]
Public ActionResult Logout(){
AuthManager. SignOut();
return RedirectToAction("Index", "Home");
}
Private  IAuthenticationManager AuthManager { get {
return HttpContext.GetOwinContext().Authentication;
}
}
Private AppUserManager UserManager { get {
return HttpContext.GetOwinContext().GetUserManager< AppUserManager >();

}
}
}


شکل زیر بعد از درخواست آدرس های /Home/Indexand و Home/ OtherAction پاسخ های تولید شده برای کاربر Bob نشان داده شده است.


دوره آموزش MVC 5
نکته :

role ها زمانی بارگیری می شوند که کاربر وارد برنامه شود. این یعنی اگر شما role های کاربری که در حال حاضر احراز هویت شده است را تغییر دهید تا زمانی که از برنامه خارج شوید و مجددا آن کاربر را احراز هویت کنید، این تغییرات اعمال نمی شوند.


پیگردی دیتابیس

یکی از مشکلات اساسی در نمونه پروژه ی من این است که دسترسی به کنترلرهای RoleAdmin و Admin من محدود نیست. این همان مثال معروف مرغ و تخم مرغ است. زیرا اگر بخواهم دسترسی را محدود کنم باید کاربران و role ها را ایجاد کنم، اما این دو کنترلر ابزارهای مدیریت کاربر هستند و اگر آن ها را دور از دسترس Authorizeattribute قرار دهم، دیگر اطلاعات محرمانه ای باقی نخواهد ماند که بتوانم با کمک آن ها به این دو کنترلر دسترسی پیدا کنم. مخصوصا برای بار اولی که برنامه را مستقر (deploy) کنم.
راه حل این مشکل پیگردی دیتابیس به کمک برخی از داده های اولیه بعد از ایجاد طرح توسط ویژگی Entity Framework Code First است. با کمک این کار می توانم به صورت خودکار کاربران را ایجاد کنم و به گونه ای آن ها را به role ها تخصیص دهم که یک سطح پایه ای از محتوا در دیتابیس وجود داشته باشد.
برای پیگردی دیتابیس باید دستوراتی را به متد PerformInitialSetup مربوط به کلاس IdentityDbInit اضافه کنید. این کلاس دیتابیس Entity Framework مختص به برنامه است. در قطعه کد زیر می توانید تغییراتی که من برای ایجاد یک administration user اعمال کرده ام را مشاهده کنید.

using System.Data.Entity;
using Microsoft.AspNet.Identity.EntityFramework;
using Users.Models;
using Microsoft.AspNet.Identity;

namespace Users.Infrastructure {
public class AppIdentityDbContext : IdentityDbContext< AppUser > {
public AppIdentityDbContext() : base("IdentityDb") { } static AppIdentityDbContext() {
Database.SetInitializer< AppIdentityDbContext >(new IdentityDbInit());
}

public static AppIdentityDbContext Create() {
return new AppIdentityDbContext();
}
}
public class IdentityDbInit : DropCreateDatabaseIfModelChanges< AppIdentityDbContext > { 
protected override void Seed(AppIdentityDbContext context) {
PerformInitialSetup(context); base.Seed(context);
}
public void PerformInitialSetup(AppIdentityDbContext context) {
AppUserManager userMgr = new AppUserManager(new UserStore< AppUser >(context));
AppRoleManager roleMgr = new AppRoleManager(new RoleStore< AppRole >(context));
string roleName = "Administrators"; string userName = "Admin";
string password = "MySecret";
string email = "admin@example.com";
if(!roleMgr.RoleExists(roleName)) {
roleMgr.Create(newAppRole(roleName));
}

AppUser user = userMgr.FindByName(userName);
if (user == null) {
userMgr.Create(new AppUser { UserName = userName, Email = email }, password);
user = userMgr.FindByName(userName);
}
if (!userMgr.IsInRole(user.Id, roleName)){
userMgr.AddToRole(user.Id, roleName);
}
}
}
}


نکته :

دراین مثال برای شناسایی موقعیت و مدیریت role و کاربر از متدهای گسترشی همگام استفاده کرده ام. همانطور که در فصل 13 توضیح دادم، استفاده ی پیش فرض متدهای ناهمگام را نسبت به متدهای همگام ترجیح می دهم. اما متدهای همگام در مواقعی که نیاز به انجام رشته ای از عملیات های مرتبط داشه باشید می توانند مفید باشند.

با توجه به اینکه متد PerformInitialSetup قبل از تکمیل پیکربندی OWIN فراخوانی می شود، من به صورت مستقیم نمونه هایی از AppUserManager و AppRoleManager را ایجاد کرده ام. از اشیاء RoleManager و AppManager برای ایجاد role ای به نام Administrators و کاربری به نام Admin استفاده کرده ام و کاربر را به role اضافه کرده ام.

نکته :

قبل از آن که کار پیگردی دیتابیس را به پروژه ی خود اضافه کنید، فصل 15 را مطالعه کنید. در این فصل انتقال مکان دیتابیس را توصیف کرده ام، که به کمک این شیوه می توانید کنترل تغییرات طرح را در دیتابیس در دست گرفته و منطق پیگردی را در مکان دیگری قرار دهید.


بعد از انجام این تغییر، برای حفاظت از کنترلرهای RoleAdmin و Admin می توانم از Authorizeattribute استفاده کنم. قطعه کد زیر تغییراتی که من بر روی Admincontroller اعمال کرده ام را نشان می دهد.

using System.Web;
using System.Web.Mvc;
using Microsoft.AspNet.Identity.Owin;
using Users.Infrastructure;
using Users.Models;
using Microsoft.AspNet.Identity;
using System.Threading.Tasks;
namespace Users.Controllers {
[Authorize(Roles = "Administrators")]
public class AdminController : Controller {
// ...دستورات حذف شده برای کوتاه کردن کار...
}
}


قطعه کد زیر تغییرات متناظری که من بر روی RoleAdmincontroller اعمال کرده ام را نشان می دهد.

using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Users.Infrastructure;
using Users.Models;
using System.Collections.Generic;
namespace Users.Controllers {
[Authorize(Roles = "Administrators")]
public class RoleAdminController : Controller {
// ... دستورات حذف شده برای کوتاه کردن کار...
}
}

تا زمانی که طرح ایجاد نشود، کار پیگردی دیتابیس انجام نمی شود. این یعنی من برای تکمیل این فرآیند باید دیتابیس را ریست کنم. در برنامه های واقعی شما نیازی به انجام این کار ندارید، اما قصد من این بود که تا قبل از ایجاد حساب کاربری مدیریتی و تا زمانی که چگونگی انجام احراز هویت و اختیاردهی به کاربر را توضیح ندهم، صبر کنم.
برای پاک کردن دیتابیس، پنجره ی Visual Studio SQL Server Object Explorer را باز کنید، آیتم IdentityDb را پیدا کنید و بر روی آن راست کلیک کنید. delete را از منوی باز شده انتخاب کنید و هر دو گزینه ی مربوط به پاک کردن دیتابیس را انتخاب کنید. برای پاک کردن دیتابیس بر روی دکمه ی OK کلیک کنید.
حالا دیتابیسی خالی ایجاد کنید که قرار است طرح را به آن اضافه کنید. برای انجام این کار بر روی آیتم دیتابیس کلیک کنید، Add New Database را انتخاب کنید و IdentityDb را در بخش مربوط به اسم دیتابیس وارد کنید. برای ایجاد این دیتابیس خالی OK را بزنید.

نکته :

دستورالعمل های گام به گام به همراه اسکرین شات هایی در فصل 13 در رابطه با ایجاد دیتابیس وجود دارد.

حالا برنامه را اجرا کنید و آدرس /Admin/Indexor یا /RoleAdmin/Index را درخواست کنید. با توجه به اینکه طرح را ایجاد کرده اید و دیتابیس را پیگردی کرده اید، با اندکی تاخیر مواجه می شوید. اما به هر حال بعد از گذشت زمانی از شما خواسته می شود تا نام کاربری و رمز عبور خود را وار دکنید. به عنوان اسم از Admin و به عنوان رمز عبور از MySecretas استفاده کنید تا بتوانید به کنترلرها دسترسی پیدا کنید.
هشدار پاک کردن دیتابیس باعث حذف شدن حساب های کاربری ای می شود که شما با استفاده از جزئیات موجود در جدول 10-14 ایجاد کرده اید. به همین دلیل است که این کار را نباید در یک دیتابیس زنده شامل جزئیات کاربری انجام داد.

خلاصه

در این فصل چگونگی بهره گیری از ASP.NET Identity جهت احراز هویت و اختیاردهی به کاربران را به شما نشان دادم. توضیح دادم که چگونه رویدادهای چرخه ی عمر ASP.NET بنیانی برای درخواست های احراز هویتی فراهم می کنند، چگونه اطلاعات محرمانه ی کاربران را گردآوری و ارزیابی کنید و چگونه دسترسی به action method ها را بر اساس role هایی که کاربر عضو آن ها است محدود کنید. در فصل بعد به برخی از ویژگی های پیشرفته ی موجود در خواهم پرداخت.


برای مطالعه سرفصل آموزش پیشرفته MVC کلیک نمایید .

1397/07/04 3217 763
رمز عبور : tahlildadeh.com یا www.tahlildadeh.com
نظرات شما

نظرات خود را ثبت کنید...