ASP.NET Identity教程三:(用户授权2)角色管理
1. 添加角色
角色管理的第一步,就是先创建一个角色,然后基于该角色可以进行后面的授权操作。在 ASP.NET Identity 中,对角色的管理非常简单,只要配置好 RoleManager,就可以从OWIN上下文中获取到RoleManager,然后执行相应的CRUD操作即可。
1.1 编写 AddRoleViewModel
在 IdentityRole 中,只有一个 Name 字段可用,所以我们在 AddRoleViewModel中,也只有一个 Name 属性。
在“ViewModels”文件夹中添加一个名称为“AddRoleViewModel.cs”的类文件,然后编写如下代码:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.ComponentModel.DataAnnotations; namespace jsdhh2.ViewModels { public class AddRoleViewModel { [Required] [StringLength(20)] [Display(Name = "角色名称")] public string Name { get; set; } } }
3.2. 编写 Add 方法
在“RoleController”控制器中编写 2 个 Add()方法,一个是 Get,一个是 Post。如下
代码:
/// <summary> /// 添加角色 /// </summary> /// <returns></returns> [HttpGet] public ActionResult Add() { return View(); } /// <summary> /// 添加角色 /// </summary> /// <param name="model"></param> /// <returns></returns> [HttpPost] [ValidateAntiForgeryToken] public async Task<ActionResult> Add(AddRoleViewModel model) { if (ModelState.IsValid) { var result = await RoleManager.CreateAsync(new Models.Role(model.Name)); if (result.Succeeded) { return RedirectToAction("List"); } AddErrors(result); } return View(); }
使用 RoleManager.CreateAsync()异步方法,就可以添加角色名称到数据库中。
3.23.3. 编写 Add 视图:Views”“Role”文件夹中添加一个名称为“Add.cshtml”视图,然后编写如下代码
@model jsdhh2.ViewModels.AddRoleViewModel @{ ViewBag.Title = "添加角色"; } @using (Html.BeginForm("Add", "Role", FormMethod.Post, new { @class = "form-horizontal", role = "form" })) { @Html.AntiForgeryToken() <h4>添加角色。</h4> <hr /> @Html.ValidationSummary("", new { @class = "text-danger" }) <div class="form-group"> @Html.LabelFor(m => m.Name, new { @class = "col-md-2 control-label" }) <div class="col-md-10"> @Html.TextBoxFor(m => m.Name, new { @class = "form-control" }) </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <input type="submit" class="btn btn-default" value="保存" /> </div> </div> }
上面的 ViewModel、方法和视图都编写完成之后,就可以运行 Add()方法和视图了,效果如图 8-5 所示
我的执行错误,提示了code first.呵,可以将原数据库删除,重新执行就可以。
未找到视图“List”或其母版视图,或没有视图引擎支持搜索的位置。搜索了以下位置:
~/Views/Role/List.aspx
~/Views/Role/List.ascx
~/Views/Shared/List.aspx
~/Views/Shared/List.ascx
~/Views/Role/List.cshtml
~/Views/Role/List.vbhtml
~/Views/Shared/List.cshtml
~/Views/Shared/List.vbhtml
这是正常的,我们没有LIST视图。
可我们查看数据库,发现已正成的注入数据。
当角色添加成功后,就会转到 Role/List 页面,由于我们还没有编写 List()方法和视图,所以图 8-7 是访问不到的,也就出现了 HTTP 404 错误,但是有一点可以肯定,我们的角色名称添加成功了。
看到了3个字段,其中Discriminator字段是在添加角色名称时出现的。
4. 角色列表
现在我们来创建角色列表,用来显示所有添加的角色名称。
4.1. 编写 List 方法
在“RoleController”控制器中编写一个带有 HttpGet 的 List()方法,用于显示所有的角色名称。如下代码:
/// <summary> /// 所有角色名称 /// </summary> /// <returns></returns> [HttpGet] public ActionResult List() { var roleList = RoleManager.Roles.ToList(); return View(roleList); }
在 List()方法中使用了 RoleManager.Roles.ToList()来获取所有的角色信息。这里没有使用 ViewModel,直接使用了 Yidosoft.Identity.Models.Role 作为 Model。
4.2. 编写 List 视图:
在“Views”“Role”文件夹中添加“List.cshtml”视图,并编写如下代码
@model IEnumerable<Yidosoft.Identity.Models.Role> @{ ViewBag.Title = "角色列表"; } <h4>角色列表</h4> <p> @Html.ActionLink("添加角色", "Add") </p> <table class="table"> <thead> <tr> <th>角色名称</th> </tr> </thead> <tbody> @foreach (var role in Model) { <tr> <td>@Html.DisplayFor(m => role.Name)</td> <td> @Html.ActionLink("编辑", "Edit", new { id = role.Id }) @Html.ActionLink("删除", "Del", new { id = role.Id }) @Html.ActionLink("添加用户", "UserToRole", new { id = role.Id }) @Html.ActionLink("查看用户", "ViewRoleUser", new { id = role.Id }) </td> </tr> } </tbody> </table>
这里使用了 table 来显示角色列表,并且还添加了编辑、删除和添加角色链接,方便在角色列表中操作。现在运行一下,并刷新图 8-7 的页面,如图 8-9 所示:
在图 8-9 中已经将之前添加的角色名称显示出来了。点击此页面上的“添加角色”就可以继续添加其它角色名称。
5. 编辑角色
点击“编辑”链接就可以转到编辑页面对当前角色进行编辑,也就是修改角色的名称。
5.1. 编写 EditRoleViewModel
在“ViewModels”文件夹中添加“EditRoleViewModel.cs”类文件,并编写如下代码:
using System.ComponentModel.DataAnnotations; namespace jsdhh2.ViewModels { public class EditRoleViewModel { [Required] [StringLength(20)] [Display(Name = "角色名称")] public string Name { get; set; } } }
编辑角色时,只是对角色名称进行修改。
2. 编写 Edit 方法
使用 Edit()方法执行角色名称的编辑操作。在“RoleController”控制器中编写两个Edit()方法,一个带有 HttpGet 特性,一个带有 HttpPost 特性。如下代码:
[HttpGet] public ActionResult Edit(string id) { if (string.IsNullOrEmpty(id)) { return new HttpStatusCodeResult(System.Net.HttpStatusCode.BadRequest); } Models.Role role = RoleManager.FindById(id); if (role == null) { return HttpNotFound(); } var editModel = new EditRoleViewModel() { Name = role.Name }; return View(editModel); } /// <summary> /// 编辑角色 /// </summary> /// <param name="id"></param> /// <param name="model"></param> /// <returns></returns> [HttpPost] [ValidateAntiForgeryToken] public async Task<ActionResult> Edit(string id, EditRoleViewModel model) { if (ModelState.IsValid && !string.IsNullOrEmpty(id)) { var role = RoleManager.FindById(id); role.Name = model.Name; var result = await RoleManager.UpdateAsync(role); if (result.Succeeded) { return RedirectToAction("List"); } AddErrors(result); } return View(model); }
对于 HttpGet 的 Edit()方法,会根据 URL 参数上的 id 值获取当前表单数据。对于HttpPost 的 Edit()方法,则可以将修改后的表单数据提交到服务器上。
5.3. 编写 Edit 视图
现在添加对应于 Edit()方法的默认视图 Edit.cshtml。在“Views”“Role”文件夹中添加 Edit.cshtml 视图,并编写如下代码
@model jsdhh2.ViewModels.EditRoleViewModel @{ ViewBag.Title = "编辑角色"; } @using (Html.BeginForm("Edit", "Role", FormMethod.Post, new { @class = "form-horizontal", role = "form" })) { @Html.AntiForgeryToken() <h4>编辑角色。</h4> <hr /> @Html.ValidationSummary("", new { @class = "text-danger" }) <div class="form-group"> @Html.LabelFor(m => m.Name, new { @class = "col-md-2 control-label" }) <div class="col-md-10"> @Html.TextBoxFor(m => m.Name, new { @class = "form-control" }) </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <input type="submit" class="btn btn-default" value="提交" /> </div> </div> }
将 EidtRoleViewModel、Edit()方法和 Edit 视图的代码编写完成之后,就可以运行一下结果。在图 8-9 中点击“编辑”链接,如图 8-10 所示:
可见,角色名称已经修改成功了
6. 删除角色
删除角色是指将角色从数据库中删除,由于目前为止还没有将用户关联到角色,所以可以直接将角色删除掉,如果角色已关联到用户,则需要先解除关联,再删除角色。
6.1. 编写 Delete 方法
这里我们不使用 ViewModel 和视图来删除角色,直接在角色列表上删除,删除完成后再返回到角色列表上。
在“RoleController”控制器中编写 Del()方法,用于完成删除操作。如下代码:
/// <summary> /// 删除角色 /// </summary> /// <param name="id"></param> /// <returns></returns> [HttpGet] public async Task<ActionResult> Del(string id) { if (string.IsNullOrEmpty(id)) { return new HttpStatusCodeResult(System.Net.HttpStatusCode.BadRequest); } var role = RoleManager.FindById(id); if (role == null) { return HttpNotFound(); } var result = await RoleManager.DeleteAsync(role); if (!result.Succeeded) { AddErrors(result); } return RedirectToAction("List"); }
删除角色时,需要提供一个角色的 id 值,然后就可以使用
RoleManager.DeleteAsync()就可以删除角色了。删除角色只编写一个带有 HttpGet特性的 Del()方法即可。现在运行一下,并打开 Role/List 页面。如图 8-13 所示:
可见,当点击“删除”链接之后,直接又返回到了 List 页面,并且之前的角色给删除掉了,在数据库中也不存在了。
可是一点就删了,乱一误点了哩?
@Html.ActionLink("删除", "Del", new { id = role.Id }, new { @class = "btn btn-warning", onclick = "return confirm('你确定要删除吗?')" })
在Del链接那,加上一个弹窗即可。
7. 向角色添加用户
角色的最大功能就是能与用户关联,使关联到的用户具有同样的权限,这样我们在授予权限时,只给角色授权即可。
7.1. 添加链接
我们还是在角色列表中操作向角色添加用户,因此,需要添加一个链接,并需要
传递角色 Id 参数。
打开“Views”“Role”中的“List.cshtml”视图,然后添加如下代码:
<td>@Html.DisplayFor(m => role.Name)</td> <td> @Html.ActionLink("编辑", "Edit", new { id = role.Id }) @Html.ActionLink("删除", "Del", new { id = role.Id }) @Html.ActionLink("添加用户", "UserToRole", new { id = role.
在表格中添加了“添加用户”的链接,使用的操作方法是 UserToRole。然后在每个角色名称行的后面,都有一个“添加用户”的链接,点击该链接就可以给当前角色添加用户。
.7.2. 编写 UserToRole 方法
由于在操作角色用户时,需要在“RoleController”控制器中使用 UserManager,所以需要编写如下代码:
private UserManager _userManager; public UserManager UserManager { get { return _userManager ?? HttpContext.GetOwinContext().GetUserManager<UserManager>(); } private set { _userManager = value; } }
在“RoleController”控制器中编写 UserToRole()方法的代码如下:
/// 用户到角色列表 /// </summary> /// <param name="id"></param> /// <returns></returns> [HttpGet] public ActionResult UserToRole(string id) { if (string.IsNullOrWhiteSpace(id)) { return new HttpStatusCodeResult(System.Net.HttpStatusCode.BadRequest); } var role = RoleManager.FindById(id); ViewBag.RoleName = role.Name; ViewBag.RoleId = id; if (role == null) { return HttpNotFound(); } var memberIDs = role.Users.Select(x => x.UserId).ToArray(); var members = UserManager.Users.Where(x => memberIDs.Any(y => y == x.Id)); var membersNo = UserManager.Users.Except(members); return View(membersNo); }
在此方法中,使用了 role.Users 获取该角色中已经关联到的所有用户数据。然后再使用 UserManager.Users.Except()得到没有关联到角色的所有用户列表,这样我
们就可以执行添加用户操作,将没有关联到角色的用户添加到角色中。对于已经关联过的用户,将不再显示。
7.3. 编写 UserToRole 视图
在 UserToRole 视图中,以列表的形式将未添加到角色的用户显示出来。在“Views”“Role”文件夹中添加“UserToRole.cshtml”视图,并编写如下代码:
@model IEnumerable<jsdhh2.Models.User> @{ ViewBag.Title = "角色关联用户"; } <h4>给 @ViewBag.RoleName 添加用户</h4> <p> @Html.ActionLink("角色列表", "List") </p> <table class="table"> <thead> <tr> <th>用户名</th> <th>电子邮件</th> <th>QQ号码</th> <th>微信号码</th> </tr> </thead> <tbody> @foreach (var user in Model) { <tr> <td>@Html.DisplayFor(m => user.UserName)</td> <td>@Html.DisplayFor(m => user.Email)</td> <td>@Html.DisplayFor(m => user.QQ)</td> <td>@Html.DisplayFor(m => user.PhoneNumber)</td> <td> @Html.ActionLink("添加到角色", "AddToRole", new { userId = user.Id, roleId = ViewBag.RoleId, roleName = ViewBag.RoleName }) </td> </tr> } </tbody> </table>
了操作方法和视图,就可以运行了,先看一下效果。打开 Role/List 页面,如图8-15 所示,然后点击“添加用户”连接,如图 8-16 所示:
在图 8-16 中已经看到可以添加到“创建权限”角色的所有用户,点击后面的“添加到用户”按钮,就可以实现将当前用户添加到角色中。
7.4. 编写 AddToRole 方法
在图 8-16 中点击“添加到角色”会执行 AddToRole()方法,从而实现将用户添加到角色的操作
/// <summary> /// 添加用户到角色 /// </summary> /// <param name="userId"></param> /// <param name="roleName"></param> /// <param name="roleId"></param> /// <returns></returns> public ActionResult AddToRole(string userId, string roleName, string roleId) { if (!string.IsNullOrWhiteSpace(userId) && !string.IsNullOrWhiteSpace(roleName)) { UserManager.AddToRole(userId, roleName); } return RedirectToAction("UserToRole", new { id = roleId }); }
在 ASP.NET Identity 中,只需要使用 UserManager.AddToRole()方法,提供 UserId和 RoleName 的值,就可以很容易的实现将用户与角色关联起来。此操作不需要视图,因为操作完成后直接就返回到 UserToRole 操作上了。现在编码都完成了,看一下效果,在图 8-16 中将“001@yidosoft.cn”用户添加到“创建权限”角色。如图 8-17 所示:
当将某个用户添加到角色之后,在图 8-17 中就看不到了。能看到的都是还没有添加到角色的用户。在数据库中,用户与角色的关联是存储在“AspNetUserRoles”表中的,现在我们查
看一下,如图 8-18 所示:
从图 8-18 上看,在“AspNetUserRoles”表中,只存储了 UserId 和 RoleId,这样就存在关联了。
8. 查看角色用户
在图 8-17 中添加用户到角色之后,我们怎么能看到这些用户呢?我们现在就实现这个功能,使我们能清楚的看到某个角色已经关联了几个用户
.1. 添加链接
同样,我们也需要在“List.cshtml”视图中添加一个“查看用户”的链接。如下代码
@Html.ActionLink("查看用户", "ViewRoleUser", new { id = role.Id }, new { @class = "btn btn-success" })
8.2. 编写 ViewRoleUser 方法
在“RoleController”控制器中添加 ViewRoleUser()方法,并编写如下代码:
/// 查看角色中的用户 /// </summary> /// <param name="id"></param> /// <returns></returns> public ActionResult ViewRoleUser(string id) { if (string.IsNullOrWhiteSpace(id)) { return new HttpStatusCodeResult(System.Net.HttpStatusCode.BadRequest); } var role = RoleManager.FindById(id); ViewBag.RoleName = role.Name; var memberIDs = role.Users.Select(x => x.UserId).ToArray(); var members = UserManager.Users.Where(x => memberIDs.Any(y => y == x.Id)); return View(members); }
使用 UserManager.Users.Where()方法将所有与当前角色关联的用户过滤出来。
8.3. 编写 ViewRoleUser 视图
在“Views”“Role”文件夹中添加“ViewRoleUser.cshtml”视图,并编写如下代码:
@model IEnumerable<jsdhh2.Models.User> @{ ViewBag.Title = "角色关联的用户"; } <h4>查看 @ViewBag.RoleName 中的用户</h4> <p> @Html.ActionLink("角色列表", "List") </p> <table class="table"> <thead> <tr> <th>用户名</th> <th>电子邮件</th> <th>QQ号码</th> <th>微信号码</th> </tr> </thead> <tbody> @foreach (var user in Model) { <tr> <td>@Html.DisplayFor(m => user.UserName)</td> <td>@Html.DisplayFor(m => user.Email)</td> <td>@Html.DisplayFor(m => user.QQNumber)</td> <td>@Html.DisplayFor(m => user.WechatNumber)</td> <td> @Html.ActionLink("移除用户", "RemoveRoleUser", new { roleName = ViewBag.RoleName, userId = user.Id }) </td> </tr> } </tbody> </table>
现在运行一下效果,在图 8-19 中点击“查看用户”,如图 8-20 所示:
9 角色授权
在上面关于角色的相关操作已经完成了,现在我们就可以使用角色授权了,还拿前面的“TestAuthorizeController”控制器作为例子。如下代码:
[Authorize(Roles ="创建权限")] public ActionResult Index() { return View(); } [AllowAnonymous] public ActionResult Home() { return View(); } }
在此代码中,给 Index()方法使用了:[Authorize(Roles ="创建权限")]
来授予权限,这样对于 Index()方法,只有“创建权限”角色中的用户可以访问,现在使用“003@yidosoft.cn”访问一下,如图 8-21 所示:
可见,直接转到了登录页面,表示没有权限访问。现在我们将“003@yidosoft.cn”用户添加到“创建权限”角色,如图 8-22 所示:
然后再访问“/TestAuthorize/Index”,如图 8-23 所示:再次访问就看见了。
可见,已经可以正常访问了。使用角色授权,我们只需要将角色规划好,然后在角色中添加用户就可以了。这样不但易于维护,使用起来也比较方便。
10. 移除用户
移除用户是指将指定的用户从某个角色中移除,移除后该用户就再具有角色的权限。
我们在 8.8 节中的“查看角色用户”列表中添加移除功能,在“ViewRoleUser.cshtml”视图中添加一个“移除用户”的链接,点击之后将该用户从角色中移除掉。代码如
@Html.ActionLink("移除用户", "RemoveRoleUser", new { roleName=ViewBag.RoleName, userId = user.Id })
“创建权限”角色中共有 2 个用户,我们可以点击每个用户后面的“移除用户”链接将用户从该角色中移除,也就是取消角色与用户的关联。
现在编写移除用户的逻辑,在“RoleController”控制器中,编写一个
“RemoveRoleUser()”方法,使用 HttpGet 请求,代码如下:
/// <param name="userId"></param> /// <returns></returns> [HttpGet] public ActionResult RemoveRoleUser(string roleName, string userId) { if (string.IsNullOrWhiteSpace(roleName) && string.IsNullOrWhiteSpace(userId)) { return new HttpStatusCodeResult(System.Net.HttpStatusCode.BadRequest); } var result = UserManager.RemoveFromRole(userId, roleName); var role = RoleManager.FindByName(roleName); if (!result.Succeeded) { return View("Error"); } return RedirectToAction("ViewRoleUser", new { id = role.Id }); }
在此代码中,使用了 UserManager.RemoveFromRole()将指定的用户从指定的角色中移除掉。移除成功后,直接又转到了“ViewRoleUser()”操作方法上。在图 8-24 中将“001@yidosoft.cn”用户移除掉,点击“移除用户”链接,
当点击过某个用户的“移除用户”之后,就会在图 8-25 中消失,表示与指定的角色解除了关联。至此,我们已经完成了 ASP.NET Identity 中的基本配置、用户管理、角色管理和
授权,这也是 ASP.NET Identity 的核心功能,也是在网站中最常用的功能,这里只是讲解了如何使用 ASP.NET Identity 的相关 API 去完成相应的功能,至于更多的功能,只需要按此方法举一反三就可以轻易的实现,方法很重要。