ASP。NETCoreIdentity系列之五
这节我们将介绍在Identity中如何使用Role,在我们应用程序中可以通过ASP.NET Core Identity创建Roles并且该角色可以包含一系列权限来执行应用程序的一系列活动
例如:一个组织可以有4个角色:
1.Admin – 管理员角色给员工分配工作
2.Manager –查看客户需求并按时完成项目
3.Network –用于保持组织的互联网以安全的方式运行。
4.Security - 系统安全相关权限
在ASP.NET Core Identity 我们能创建任何数量的Roles并且可以将这些Roles赋值给Identity Users
1、ASP.NET Core Identity RoleManager类
我们使用ASP.NET Core Identity RoleManager来管理Role,RoleManager 泛型版本T表示Identity在数据库中的Roles RoleManager定义了一些重要的功能和属性:
名称
描述
CreateAsync(role)
创建一个新的角色
DeleteAsync(role)
删除一个指定的角色
FindByIdAsync(id)
根据角色Id查找一个角色
FindByNameAsync(name)
根据角色名称查找一个角色
RoleExistsAsync(name)
根据角色名称检查角色是否存在
UpdateAsync(name)
更新角色
Roles
返回Identity中的所有角色
2、Identity中角色管理 我们使用ASP.NET Core Identity实现一个创建和删除角色的功能,创建一个名字为RoleController.cs并且添加如下代码: public class RoleController : Controller{ private RoleManager _roleManager; public RoleController(RoleManager roleManager) { _roleManager = roleManager; } public IActionResult Index() => View(_roleManager.Roles); private void Errors(IdentityResult result) { foreach (IdentityError error in result.Errors) ModelState.AddModelError("", error.Description); }}
在RoleController中,通过构造函数注入了RoleManager类,我们可以通过依赖注入获取到该类,并使用它来管理Identity角色 private RoleManager _roleManager;public RoleController(RoleManager roleManager){ _roleManager = roleManager;}获取所有Identity的角色
RoleManager类Roles属性提供了Identity所有的角色,我们将所有的Roles作为模型类传递给Index视图,代码如下: public IActionResult Index(){ return View(_roleManager.Roles);}接下来我们在View->Role文件夹下创建一个Index.cshtml文件 @using Microsoft.AspNetCore.Identity;@model IEnumerable@{ ViewData["Title"] = "Roles";} 新增 编号 | 角色名称 | 用户 | 编辑 | 删除 |
@foreach (var role in Model) { @role.Id | @role.Name | | 编辑 | |
}
这个视图中获取了一个IEnumerable类型集合,它将包含Identity所有Role,我们通过foreach循环将所有Role展示在table内,注意i-role我们使用了第三方Attribute,这个Attribute将调用客户自定义的TagHelper,这个特性会修改td并显示当前角色的用户列表
接下来在项目根目录一下创建一个文件夹CustomTagHelpers,在该文件夹下添加一个RoleUsersTH.cs类,并且该类继承TagHelper,这个类提供自定义的CustomerTagHelper /// /// 自定义TagHelper /// [HtmlTargetElement("td", Attributes = "i-role")] public class RoleUsersTH : TagHelper { private UserManager _userManager;
private RoleManager _roleManager; public RoleUsersTH(UserManager userManager, RoleManager roleManager) { _userManager = userManager; _roleManager = roleManager; } [HtmlAttributeName("i-role")] public string Role { get; set; } = !;
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) { List names = new List(); var role = await _roleManager.FindByIdAsync(Role); if (role != ) { foreach (var user in _userManager.Users) { if (user != && await _userManager.IsInRoleAsync(user, role.Name ?? "")) names.Add(user.UserName ?? ""); } }
output.Content.SetContent(names.Count == 0 ? "No Users" : string.Join(", ", names)); } }
客户自定义的TagHelper操作td中i-role特性,这个特性获取对应的角色ID,并在后台被处理
我们需要更新_ViewImpo rts.cshtml文件
ASP.NET Core Identity创建角色
我们使用RoleManager的CreateAsync方法创建一个Identity Role,在RoleController类中创建一个CreateAsync方法,代码如下: public IActionResult Create() => View();[HttpPost]public async Task CreateAsync([Required] string name){ if (ModelState.IsValid) { var result = await _roleManager.CreateAsync(new IdentityRole(name)); if (result.Succeeded) return RedirectToAction("Index"); else Errors(result); } return View(name);}
CreateAsync方法入参是name(角色名称)的字符串参数并且使用RoleManager的CreateAsync()方法来创建一个Identity Role var result = await _roleManager.CreateAsync(new IdentityRole(name));我们接下来在Views->Role目录下添加一个Create 视图,代码如下: @model IdentityRole@{ ViewData["Title"] = "新增角色";}
ASP.NET Core Identity删除角色
我们使用RoleManager的DeleteAsync()来删除一个Identity Role, 在RoleController.cs的控制器中创建一个DeleteAsync 方法并接受一个角色id(需要删除的角色),代码如下: public async Task DeleteAsync(string id){ var role = await _roleManager.FindByIdAsync(id); if (role != ) { var identityResult = await _roleManager.DeleteAsync(role); if (identityResult.Succeeded) { return RedirectToAction("Index"); } else { Errors(identityResult); } } else { ModelState.AddModelError("", "No role found"); } return View("Index", _roleManager.Roles);}
测试Identity创建和删除角色功能 运行应用程序并将URL导航 https://localhost:7296/Role/Creat e每次创建完一个角色就会跳转到Index View显示Identity数据库中所有的角色 创建role页面如下:
我们可以通过这个删除按钮删除Identity 数据库中的Role:
3、添加用户到Roles或从Roles中移除用户 现在我们创建一个新的功能,将完成两件事情: 添加用户到指定角色 将用户从角色中移除
为了实现这个功能,我们在Models文件加下添加两个类,分别为RoleEdit和RoleModification public class RoleEdit{ public IdentityRole? Role { get; set; } public IEnumerable? Members { get; set; } public IEnumerable? NoMembers { get; set; }}
RoleEdit表示一个角色和他关联的用户以及和该角色未关联的用户,RoleModification这个类将帮助我们修改一个角色,具体定义如下: public class RoleModification{ [Required] public string RoleName { get; set; } = !;
public string RoleId { get; set; } = !;
public string[]? AddIds { get; set; }
public string[]? DeleteIds { get; set; }}
这两个类帮助我们将一个用户添加到角色中和从角色中移除用户,我们修改一些RoleController类,添加UpdateAsync方法, 下 面Get版本的UpdateAsync方法查询两部分数据,属于该角色的用户和不属于该角色的用户 public async Task UpdateAsync(string id){ var role = await _roleManager.FindByIdAsync(id); List members = new List(); List nonMembers = new List(); foreach (var appUser in _userManager.Users) { var list = await _userManager.IsInRoleAsync(appUser, role?.Name ?? "") ? members : nonMembers; list.Add(appUser); } return View(new RoleEdit() { Role = role, Members = members, NoMembers = nonMembers });}下面Post版本的UpdateAsync方法表示给用户添加和移除角色 [HttpPost] public async Task UpdateAsync(RoleModification roleModification) { if (ModelState.IsValid) { foreach (var userId in roleModification.AddIds ?? new string[] { }) { var appUser = await _userManager.FindByIdAsync(userId); if (appUser != ) { var identityResult = await _userManager.AddToRoleAsync(appUser, roleModification.RoleName); if (!identityResult.Succeeded) Errors(identityResult); } } foreach (var userId in roleModification.DeleteIds ?? new string[] { }) { var appUser = await _userManager.FindByIdAsync(userId); if (appUser != ) { var identityResult = await _userManager.RemoveFromRoleAsync(appUser, roleModification.RoleName); if (!identityResult.Succeeded) Errors(identityResult); } } } if (ModelState.IsValid) return RedirectToAction(nameof(Index)); else return await UpdateAsync(roleModification.RoleId); }注意我们在构造函数中添加了UserManager依赖: private RoleManager _roleManager;private UserManager _userManager;public RoleController(RoleManager roleManager, UserManager userManager){ _roleManager = roleManager; _userManager = userManager;}我们使用UserManager类的下面方法类管理ASP.NET Core Identity Roles
名称
描述
AddToRoleAsync(AppUser user, string name)
将用户添加到指定角色中
RemoveFromRoleAsync(AppUser user, string name)
从指定角色中删除用户
GetRolesAsync(AppUser user)
获取当前用户所有角色
IsInRoleAsync(AppUser user, string name)
判断一个用户是否是指定的角色成员如果是返回ture,否则false 接下来,在Views -> Role 文件夹下添加Update.csthml,代码如下: @model RoleEdit@{ ViewData["Title"] = "编辑角色";}<p asp-validation-summary="All" class="text-danger">p><style> .table-column-width td { width: 200px }style><form class="form" method="post" role="form"> <input type="hidden" name="roleName" value="@Model.Role?.Name" /> <input type="hidden" name="roleId" value="@Model.Role?.Id" /> <h2><small> @Model.Role.Name 角色包含的用户small>h2> <table class="table-column-width table table-bordered"> @if (!Model.Members!.Any()) { <tr> <td>该角色没有关联任何用户td> tr> } else { foreach (var appUser in Model.Members ?? new List<AppUser>()) { <tr> <td>@appUser.UserNametd> <td> <input type="checkbox" name="DeleteIds" value="@appUser.Id" /> td> tr> } } table> <h2><small> @Model.Role.Name 角色未包含的用户small>h2> <table class="table-column-width table table-bordered"> @if (!Model.NoMembers!.Any()) { <tr> <td>该角色保护所有用户td> tr> } else { foreach (var appUser in Model.NoMembers ?? new List<AppUser>()) { <tr> <td>@appUser.UserNametd> <td> <input type="checkbox" name="AddIds" value="@appUser.Id" /> td> tr> } } table> <button class="btn btn-primary">保存button> <button asp-action="index" class="btn btn-secondary">返回button>form>
这个页面包含两个Table: 当前角色包含的用户 当前角色未包含的用户 我们可以选择对应的 checkbox给角色添加和删除用户
测试更新功能
我们先注册三个用户(密码为:Coder77@1):
同时我们也创建了三个角色: 我们查看当前每个角色都没有用户:
一个用户可以指定多个角色,使用这个功能仅仅完成了ASP.NET Core Identity 更新角色的功能,下面我们将实现基于角色的认证 4、基于ASP.NET Core Identity Role 认证 ASP.NET Core Identity 角色可以作为认证,特定角色所包含的用户能访问特定的资源。例如:我们给指定方法设定一个[Authorize(Roles = "SomeRole")]特性,这个角色下的所有用户都能访问这个方法 在HomeController的Index方法添加Authorize特性[Authorize(Roles = "Manager")] , 指明只有Manager角色的用户才能访问HomeController的Index 方法,代码如下: public class HomeController : Controller{ private readonly ILogger _logger; private UserManager _userManager; public HomeController(UserManager userManager, ILogger logger) { _userManager = userManager; _logger = logger; } [Authorize(Roles = "Manager")] public async Task Index() { var appUser = await _userManager.GetUserAsync(HttpContext.User); var message = "Hello " + appUser?.UserName; return View((object)message); }}运行应用程序,使用tom登录,访问HomeController方法我们可以正常访问HomeController的Index方法
因为tom所拥有的角色是Manager。现在我们使用alice 用户进行登录,alice不属于Manager角色,所以当我们尝试访问Home/Index时,应用程序将会跳转到https://localhost:7296
/Account/AccessDenied?ReturnUrl=%2F,因为Account Con tr oller控制器中没有创建AccessDenied方法,因此会获取一个HTTP 404 错误,因此我们在ActionController下创建一个AccessDenied方法: public IActionResult AccessDenied(){ return View();}在Views->Account目录下添加AccessDenied.cshtml视图,代码如下: <h2>Access Deniedh2><a asp-controller="Account" asp-action="Logout" class="btn btn-primary">退出登录a>现在,运行应用程序,并进入登录页面https://localhost:7296 /Account/Login. 使用 alice账户登录:
邮箱–alice@yahoo.com
密码– Coder77@1 登录之后将被调转到Denied URL, 如下图所示:
Identity为我们提供设置了一个默认的/Account/AccessDenied URL地址,我们可以通过配置改变这个地址: builder.Services.ConfigureApplicationCookie( opts => { opts.LoginPath = "/Account/Login"; opts.AccessDeniedPath= "/Account/AccessDenied"; opts.Cookie.Name = ".AspNetCore.Identity.Application"; opts.ExpireTimeSpan = TimeSpan.FromMinutes(20); opts.SlidingExpiration = true; });
总结 这节我们主要讲解了Identity角色管理和身份认证
源代码地址:
https://github.com/bingbing-gui/Asp.Net-Core-Skill/tree/master/AspNetCore.Identity/Identity