名称 |
描述 |
CreateAsync(role) |
创建一个新的角色 |
DeleteAsync(role) |
删除一个指定的角色 |
FindByIdAsync(id) |
根据角色Id查找一个角色 |
FindByNameAsync(name) |
根据角色名称查找一个角色 |
RoleExistsAsync(name) |
根据角色名称检查角色是否存在 |
UpdateAsync(name) |
更新角色 |
Roles |
返回Identity中的所有角色 |
public class RoleController : Controller { private RoleManager<IdentityRole> _roleManager; public RoleController(RoleManager<IdentityRole> 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<IdentityRole> _roleManager; public RoleController(RoleManager<IdentityRole> roleManager) { _roleManager = roleManager; }获取所有Identity的角色
public IActionResult Index() { return View(_roleManager.Roles); }接下来我们在View->Role文件夹下创建一个Index.cshtml文件
@using Microsoft.AspNetCore.Identity; @model IEnumerable<IdentityRole> @{ ViewData["Title"] = "Roles"; } <div class="container"> <div class="row mb-3"> <div class="col-sm-3"> <a asp-action="Create" class="btn btn-primary">新增</a> </div> <div class="col-sm-3"></div> <div class="col-sm-3"></div> <div class="col-sm-3"></div> </div> <div class="row mb-3"> <div class="col-sm"> <table class="table-content-center table table-bordered"> <thead> <tr> <th>编号</th> <th>角色名称</th> <td>用户</td> <th>编辑</th> <td>删除</td> </tr> </thead> <tbody> @foreach (var role in Model) { <tr> <td>@role.Id</td> <td>@role.Name</td> <td i-role="@role.Id"></td> <td> <a class="btn btn-primary btn-sm" asp-action="Update" asp-route-id="@role.Id">编辑</a> </td> <td> <form method="post" asp-action="Delete" asp-route-id="@role.Id" role="form"> <button type="submit" class="btn btn-danger btn-sm">删除</button> </form> </td> </tr> } </tbody> </table> </div> </div> </div>这个视图中获取了一个IEnumerable<IdentityRole>类型集合,它将包含Identity所有Role,我们通过foreach循环将所有Role展示在table内,注意i-role我们使用了第三方Attribute,这个Attribute将调用客户自定义的TagHelper,这个特性会修改td并显示当前角色的用户列表
/// <summary> /// 堆代码 duidaima.com /// 自定义TagHelper /// </summary> [HtmlTargetElement("td", Attributes = "i-role")] public class RoleUsersTH : TagHelper { private UserManager<AppUser> _userManager; private RoleManager<IdentityRole> _roleManager; public RoleUsersTH(UserManager<AppUser> userManager, RoleManager<IdentityRole> roleManager) { _userManager = userManager; _roleManager = roleManager; } [HtmlAttributeName("i-role")] public string Role { get; set; } = null!; public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) { List<string> names = new List<string>(); var role = await _roleManager.FindByIdAsync(Role); if (role != null) { foreach (var user in _userManager.Users) { if (user != null && 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,并在后台被处理,我们需要更新_ViewImports.cshtml文件
public IActionResult Create() => View(); [HttpPost] public async Task<IActionResult> 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"] = "新增角色"; } <div asp-validation-summary="All" class="text-danger"></div> <form class="form-horizontal" method="post"> <div class="mb-3 row"> <div class="col-sm-1"> <label for="Name" class="control-label">角色名称:</label> </div> <div class="col-sm-11"> <input asp-for="Name" class="form-control" placeholder="请输入角色名称" /> </div> </div> <div class="mb-3 row"> <div class="col-sm-11 offset-sm-1"> <button type="submit" class="btn btn-primary">保存</button> <button asp-action="Index" class="btn btn-secondary"> 返回 </button> </div> </div> </form>
public async Task<IActionResult> DeleteAsync(string id) { //堆代码 duidaima.com var role = await _roleManager.FindByIdAsync(id); if (role != null) { 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); }
public class RoleEdit { public IdentityRole? Role { get; set; } public IEnumerable<AppUser>? Members { get; set; } public IEnumerable<AppUser>? NoMembers { get; set; } }RoleEdit表示一个角色和他关联的用户以及和该角色未关联的用户,RoleModification这个类将帮助我们修改一个角色,具体定义如下:
public class RoleModification { [Required] public string RoleName { get; set; } = null!; public string RoleId { get; set; } = null!; public string[]? AddIds { get; set; } public string[]? DeleteIds { get; set; } }这两个类帮助我们将一个用户添加到角色中和从角色中移除用户,我们修改一些RoleController类,添加UpdateAsync方法,下面Get版本的UpdateAsync方法查询两部分数据,属于该角色的用户和不属于该角色的用户
public async Task<IActionResult> UpdateAsync(string id) { var role = await _roleManager.FindByIdAsync(id); List<AppUser> members = new List<AppUser>(); List<AppUser> nonMembers = new List<AppUser>(); 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<IActionResult> UpdateAsync(RoleModification roleModification) { if (ModelState.IsValid) { foreach (var userId in roleModification.AddIds ?? new string[] { }) { var appUser = await _userManager.FindByIdAsync(userId); if (appUser != null) { 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 != null) { 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<IdentityRole> _roleManager; private UserManager<AppUser> _userManager; public RoleController(RoleManager<IdentityRole> roleManager, UserManager<AppUser> userManager) { //堆代码 duidaima.com _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 |
@model RoleEdit @{ ViewData["Title"] = "编辑角色"; } <div asp-validation-summary="All" class="text-danger"></div> <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.UserName</td> <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.UserName</td> <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给角色添加和删除用户
public class HomeController : Controller { private readonly ILogger<HomeController> _logger; private UserManager<AppUser> _userManager; public HomeController(UserManager<AppUser> userManager, ILogger<HomeController> logger) { _userManager = userManager; _logger = logger; } [Authorize(Roles = "Manager")] public async Task<IActionResult> 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
public IActionResult AccessDenied() { return View(); }在Views->Account目录下添加AccessDenied.cshtml视图,代码如下:
<h2>Access Denied</h2> <a asp-controller="Account" asp-action="Logout" class="btn btn-primary">退出登录</a>现在,运行应用程序,并进入登录页面https://localhost:7296
builder.Services.ConfigureApplicationCookie( opts => { //默认登录页面 opts.LoginPath = "/Account/Login"; opts.AccessDeniedPath= "/Account/AccessDenied"; //设置Cookie名称 opts.Cookie.Name = ".AspNetCore.Identity.Application"; //设置Cookie超时时间 opts.ExpireTimeSpan = TimeSpan.FromMinutes(20); //设置滑动时间 opts.SlidingExpiration = true; } );总结