篇(15)-入门实战-权限管理之用户创建与关联角色(ViewModel再用与模型验证一)

  • 篇(15)-入门实战-权限管理之用户创建与关联角色(ViewModel再用与模型验证一)已关闭评论
  • 110 次浏览
  • A+
所属分类:.NET技术
摘要

篇(15)-Asp.Net Core入门实战-权限管理之用户创建与关联角色(ViewModel再用与模型验证一)

篇(15)-Asp.Net Core入门实战-权限管理之用户创建与关联角色(ViewModel再用与模型验证一)

在上个篇章中,讲了角色和菜单的关系(也就是给角色赋权),本章讲用户和给用户分派角色的功能。如果是小白,最好是仔细看我写的代码,因为关键代码处都有注解。建议将篇14和篇15阅读完毕再做演练,为防止单篇过长,我将其分成2篇来讲解。

用户与角色的处理逻辑是:(1).用户的增删改查;(2).给用户选一个所属角色。

1.用户管理功能

(1).用户表(Sql库)的创建

篇(15)-入门实战-权限管理之用户创建与关联角色(ViewModel再用与模型验证一)

CREATE TABLE [dbo].[Manager]( [Id] [int] IDENTITY(1,1) NOT NULL, [RoleId] [int] NOT NULL, [UserName] [varchar](32) NOT NULL, [Password] [varchar](128) NOT NULL, [Avatar] [varchar](256) NULL, [NickName] [varchar](32) NULL, [Mobile] [varchar](16) NULL, [Email] [varchar](128) NULL, [LoginCount] [int] NULL, [LoginLastIp] [varchar](64) NULL, [LoginLastTime] [datetime] NULL, [AddManagerId] [int] NOT NULL, [AddTime] [datetime] NOT NULL, [ModifyManagerId] [int] NULL, [ModifyTime] [datetime] NULL, [IsLock] [bit] NOT NULL, [IsDelete] [bit] NOT NULL, [Remark] [varchar](128) NULL, CONSTRAINT [PK_MANAGER] PRIMARY KEY NONCLUSTERED ( [Id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO

 

(2).用户Model的编写,这个Model直接与Sql表的结构一致。

public class Manager { /// <summary> /// 主键 MaxLength属性作用于字符串,不能用在int类型上 /// </summary> [Key] public int Id { get; set; } /// <summary> /// 角色ID /// </summary> public int RoleId { get; set; } /// <summary> /// 用户名 /// </summary> [Required] public String UserName { get; set; } /// <summary> /// 密码 /// </summary> public String Password { get; set; } /// <summary> /// 头像 /// </summary> public String Avatar { get; set; } /// <summary> /// 用户昵称 /// </summary> public String NickName { get; set; } /// <summary> /// 手机号码 /// </summary> public String Mobile { get; set; } /// <summary> /// 邮箱地址 /// </summary> public String Email { get; set; } /// <summary> /// 登录次数 /// </summary> public int? LoginCount { get; set; } /// <summary> /// 最后一次登录IP /// </summary> public String LoginLastIp { get; set; } /// <summary> /// 最后一次登录时间 /// </summary> public DateTime? LoginLastTime { get; set; } /// <summary> /// 添加人 /// </summary> [Required] public int AddManagerId { get; set; } /// <summary> /// 添加时间 /// </summary> [Required] public DateTime AddTime { get; set; } /// <summary> /// 修改人 /// </summary> public int? ModifyManagerId { get; set; } /// <summary> /// 修改时间 /// </summary> [MaxLength(23)] public DateTime? ModifyTime { get; set; } /// <summary> /// 是否锁定 /// </summary> [Required] public Boolean IsLock { get; set; } /// <summary> /// 是否删除 /// </summary> [Required] public Boolean IsDelete { get; set; } /// <summary> /// 备注 /// </summary> public String Remark { get; set; } }

 

(3).用户View部分的编写

(3.1)视图View部分包括用户的增、删、改、查功能,还有对应的修改用户角色,修改用户密码。

篇(15)-入门实战-权限管理之用户创建与关联角色(ViewModel再用与模型验证一)

(3.2)Create视图代码如下

篇(15)-入门实战-权限管理之用户创建与关联角色(ViewModel再用与模型验证一)

@{ ViewData["Title"] = "新建用户"; } @model RegisterManagerView @section Scripts{ <script type="text/javascript" src="~/lib/jquery-validation/dist/jquery.validate.js"></script> <script type="text/javascript" src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script> @{ await Html.RenderPartialAsync("_ValidationScriptsPartial");} } <form action="/Manager/Create" method="post"> @Html.AntiForgeryToken() <div> <label asp-for="UserName">用户名</label> <div> <input type="text" asp-for="UserName" name="UserName" placeholder="用户名"> <span asp-validation-for="UserName" class="text-danger"></span> </div> </div> <div> <label asp-for="Password">密码</label> <div> <input type="password" asp-for="Password" name="Password" placeholder="密码" /> <span asp-validation-for="Password" class="text-danger"></span> </div> </div> <div> <label asp-for="ConfirmPassword">确认密码</label> <div> <input type="password" asp-for="ConfirmPassword" name="ConfirmPassword" placeholder="确认密码" /> <span asp-validation-for="ConfirmPassword" class="text-danger"></span> </div> </div> <div> <label asp-for="Mobile">手机号</label> <div> <input type="text" asp-for="Mobile" name="Mobile" placeholder="手机号"> <span asp-validation-for="Mobile" class="text-danger"></span> </div> </div> <div> <label asp-for="Email">Email</label> <div> <input type="text" asp-for="Email" name="Email" placeholder="邮箱"> <span asp-validation-for="Email" class="text-danger"></span> </div> </div> <div> <label asp-for="Remark">介绍</label> <div> <textarea name="Remark" asp-for="Remark" placeholder="相关介绍"></textarea> </div> </div> <div> <div> <button type="submit">确定</button> <button type="reset">重置</button> </div> </div> </form>

 

(3.3)Edit视图代码如下

篇(15)-入门实战-权限管理之用户创建与关联角色(ViewModel再用与模型验证一)

@{ ViewData["Title"] = "编辑用户"; } @model EditManagerView @section Scripts{ <script type="text/javascript" src="~/lib/jquery-validation/dist/jquery.validate.js"></script> <script type="text/javascript" src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script> @{ await Html.RenderPartialAsync("_ValidationScriptsPartial");} } <form action="/Manager/Edit" method="post"> @Html.AntiForgeryToken() <div> <label asp-for="UserName">用户名</label> <div> <input type="text" asp-for="UserName" name="UserName" placeholder="用户名"> <span asp-validation-for="UserName" class="text-danger"></span> <input type="hidden" asp-for="Id" /> </div> </div> <div> <label asp-for="Mobile">手机号</label> <div> <input type="text" asp-for="Mobile" name="Mobile" placeholder="手机号"> <span asp-validation-for="Mobile" class="text-danger"></span> </div> </div> <div> <label asp-for="Email">Email</label> <div> <input type="text" asp-for="Email" name="Email" placeholder="邮箱"> <span asp-validation-for="Email" class="text-danger"></span> </div> </div> <div> <label asp-for="Remark">介绍</label> <div> <textarea name="Remark" asp-for="Remark" placeholder="相关介绍"></textarea> </div> </div> <div> <div> <button type="submit">确定</button> <button type="reset">重置</button> </div> </div> </form>

 

(3.4)Index视图代码如下(列表页)

篇(15)-入门实战-权限管理之用户创建与关联角色(ViewModel再用与模型验证一)

@using Humanizer; @using RjWebCms.Db; @model PaginatedList<PageManager> @{ ViewData["Title"] = "用户列表"; } @section Scripts{ <script src="~/js/jquery-3.6.1.min.js"></script> <script type="text/javascript"> function DelAll() { var ids = document.getElementsByName("#chk_ids"); var arrIds = ""; var n = 0; for (var i = 0; i < ids.length; i++) { if (ids[i].checked == true) { arrIds += ids[i].value + ","; n++; } } if (n == 0) { alert("请选择要删除的信息"); return; }  arrIds = arrids.substr(0, arrIds.length - 1); // if (confirm("确定要全部删除选择用户吗")) { $.ajax({ type: "post", url: "/Manager/DeleteAll", data: { ids: arrIds }, success: function (data, state) { alert('删除成功!'); window.location.href = ""; },  error: function (data, state) { alert('删除失败'); } }); } } </script> } <div class="panel panel-default todo-panel"> @Html.AntiForgeryToken() <form asp-action="Index" method="get"> <table> <tr><td><a asp-controller="Manager" asp-action="Create">添加</a></td></tr> <tr> <td>查询关键词:<input type="text" name="SearchString" value="@ViewData["CurrentFilter"]" /></td> <td><input type="submit" value="查询" /></td> <td><a asp-action="Index">Back</a></td> <td><a id="DelAll" name="DelAll">批量删除</a></td> </tr> </table> </form> <table class="table table-hover"> <thead> <tr> <td>&#x2714;</td> <td><a asp-action="Index" asp-route-sortOrder="@ViewData["NameSortParm"]" asp-route-currentFilter="@ViewData["CurrentFilter"]">用户名</a></td> <td>角色名</td> <td>手机号</td> <td><a asp-action="Index" asp-route-sortOrder="@ViewData["DateSortParm"]" asp-route-currentFilter="@ViewData["CurrentFilter"]">时间</a></td> <td>操作</td> </tr> @foreach (var item in Model) { <tr> <td><input type="checkbox" class="done-checkbox" name="chk_ids" value="@item.Id"></td> <td>@item.UserName</td> <td>@item.RoleName</td> <td>@item.Mobile</td> <td>@item.AddTime</td> <td> <a asp-controller="Manager" asp-action="Details" asp-route-id="@item.Id">View</a> <a asp-action="Edit" asp-route-id="@item.Id">Edit</a> <a asp-action="ChangeRole" asp-route-id="@item.Id">ChangeRole</a> <a asp-action="ChangePass" asp-route-id="@item.Id">ChangePass</a> <a asp-controller="Manager" asp-action="Delete" asp-route-id="@item.Id">Delete</a> </td> </tr> } </thead> </table> @{ var prevDisabled = !Model.HasPreviousPage ? "disabled" : ""; var nextDisabled = !Model.HasNextPage ? "disabled" : ""; ; } <a asp-action="Index" asp-route-sortOrder="@ViewData["CurrentSort"]" asp-route-pageNumber="@(Model.PageIndex - 1)" asp-route-currentFilter="@ViewData["CurrentFilter"]" class="btn btn-default @prevDisabled"> 上一页 </a> <a asp-action="Index" asp-route-sortOrder="@ViewData["CurrentSort"]" asp-route-pageNumber="@(Model.PageIndex + 1)" asp-route-currentFilter="@ViewData["CurrentFilter"]" class="btn btn-default @nextDisabled"> 下一页 </a> <div class="panel-footer add-item-form"> <!-- TODO: Add item form --> </div> </div>

 

(3.5)ChangePass视图代码

篇(15)-入门实战-权限管理之用户创建与关联角色(ViewModel再用与模型验证一)

@model ChangePassView @section Scripts{ <script type="text/javascript" src="~/lib/jquery-validation/dist/jquery.validate.js"></script> <script type="text/javascript" src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script> @{ await Html.RenderPartialAsync("_ValidationScriptsPartial");} } <form action="/Manager/ChangePass" method="post"> @Html.AntiForgeryToken() <div> <label asp-for="OldPass">旧密码</label> <input type="hidden" asp-for="Id" /> <div> <input type="password" asp-for="OldPass" name="OldPass" placeholder="密码" /> <span asp-validation-for="OldPass" class="text-danger"></span> </div> </div> <div> <label asp-for="NewPass">新密码</label> <div> <input type="password" asp-for="NewPass" name="NewPass" placeholder="密码" /> <span asp-validation-for="NewPass" class="text-danger"></span> </div> </div> <div> <label asp-for="ConfirmNewPass">确认新密码</label> <div> <input type="password" asp-for="ConfirmNewPass" name="ConfirmNewPass" placeholder="确认密码" /> <span asp-validation-for="ConfirmNewPass" class="text-danger"></span> </div> </div> <div> <div> <button type="submit">确定</button> <button type="reset">重置</button> </div> </div> </form>

 

(3.6)ChangeRole视图代码

篇(15)-入门实战-权限管理之用户创建与关联角色(ViewModel再用与模型验证一)

@using RjWebCms.Models; @{ ViewData["Title"] = "修改对应角色"; } @model ChangeUserRole <form action="/Manager/ChangeRole" method="post"> @Html.AntiForgeryToken() <div> <label asp-for="Id">选择对应角色</label> <input type="hidden" asp-for="Id" /> <div> @Html.DropDownList("ddl_RoleList", ViewBag.database as IEnumerable<SelectListItem>) </div> </div> <div> <div> <button type="submit">确定</button> <button type="reset">重置</button> </div> </div> </form>

 

 

(4).用户Controller部分的实现

public class ManagerController : Controller{   private readonly IManagerService _manager;   private readonly IManagerRoleService _managerRoleService;   private readonly AppDbContext _appDbContext;   public ManagerController(IManagerService manager,IManagerRoleService managerRoleService, AppDbContext appDbContext){     _manager = manager;     _managerRoleService = managerRoleService;     _appDbContext = appDbContext;   }   public async Task<IActionResult> Index(string sortOrder, string currentFilter, string searchString, int? pageNumber){     ViewData["CurrentSort"] = sortOrder;     ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";     ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";   if (searchString != null)   {     pageNumber = 1;   }   else   {     searchString = currentFilter;   }  #region 分页操作数据 ViewData["CurrentFilter"] = searchString; var managers = from s in _appDbContext.Manager join t in _appDbContext.ManagerRole on s.RoleId equals t.Id select new PageManager{ Id=s.Id, RoleId =s.RoleId, UserName = s.UserName, Email = s.Email, Mobile = s.Mobile, AddTime = s.AddTime, RoleName = t.RoleName}; if (!string.IsNullOrEmpty(searchString)) { managers = managers.Where(s => s.UserName.Contains(searchString)); } switch (sortOrder) { case "name_desc": managers = managers.OrderByDescending(s => s.UserName); break; case "Date": managers = managers.OrderBy(s => s.AddTime); break; case "date_desc": managers = managers.OrderByDescending(s => s.AddTime); break; default: managers = managers.OrderBy(s => s.UserName); break; }  #endregion int pageSize = 4; return View(await PaginatedList<PageManager>.CreateAsync(managers.AsNoTracking(), pageNumber ?? 1, pageSize)); }  [HttpGet] public IActionResult Create() { return View(); }  [HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> Create(RegisterManagerView manager) { if (ModelState.IsValid) { Manager mUser = new Manager { UserName = manager.UserName,//但暂时用重写的方式来处理 Password = AESEncryptHelper.Encode(manager.Password.Trim(), RjWebKeys.AesEncryptKeys), //对密码加密; Mobile = manager.Mobile, Email = manager.Email, Remark = manager.Remark }; //此处可以用AutoMapper进行转换 //因为AddManagerAsync的参数是Manager对象,而非RegisterManagerView对象 //否则就需要重写一个AddManagerAsync(RegisterManagerView manager) 这样的方法 var successful = await _manager.AddManagerAsync(mUser); if (successful) return RedirectToAction("Index"); else return BadRequest("失败"); } return View(manager); }  [HttpGet] public async Task<IActionResult> Edit(int id) { if (string.IsNullOrEmpty(id.ToString())) return NotFound(); var m = await _manager.FindManagerAsync(id); if (m == null) return NotFound(); EditManagerView mView = new EditManagerView() { Id = id, UserName = m.UserName, //Password = AESEncryptHelper.Decode(m.Password,RjWebKeys.AesEncryptKeys), //解密密码 //ConfirmPassword = AESEncryptHelper.Decode(m.Password, RjWebKeys.AesEncryptKeys), //解密密码 Mobile = m.Mobile, Email = m.Email, Remark = m.Remark }; return View(mView); }  [HttpPost] public async Task<IActionResult> Edit(int id, EditManagerView editManager) { if (string.IsNullOrEmpty(id.ToString())) return NotFound(); if (ModelState.IsValid) { try { //做个对象转换 Manager mUser = new Manager { UserName = editManager.UserName, Mobile = editManager.Mobile, Email = editManager.Email, Remark = editManager.Remark }; //因为UpdateManagerAysnc方法参数为完整Manger对象,所以要转换 var result = await _manager.UpdateManagerAysnc(id, mUser); if(result) return RedirectToAction("Index"); else return BadRequest("编辑失败"); }  catch (Exception ex) { return BadRequest("编辑失败"); } }  return View(); }  public async Task<IActionResult> Details(int id) { var item = await _manager.FindManagerAsync(id); return View(item); }  public async Task<IActionResult> Delete(int id) { var result = await _manager.DeleteManagerAsync(id); if (result) return RedirectToAction("Index"); else return Ok("删除失败!"); } [HttpGet] public async Task<IActionResult> ChangePass(int id) { if (string.IsNullOrEmpty(id.ToString())) return NotFound(); //密码框的初始化也可以省略 var m = await _manager.FindManagerAsync(id); if (m == null) return NotFound(); ChangePassView cpView = new ChangePassView { Id=id, OldPass = AESEncryptHelper.Decode(m.Password, RjWebKeys.AesEncryptKeys) //解密密码m.Password, }; return View(cpView); }  [HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> ChangePass(int id,ChangePassView cheView) { if (string.IsNullOrEmpty(id.ToString())) return NotFound(); if (ModelState.IsValid) { //ChangePass方法在ManagerService中,注意参数对象 var successful = await _manager.ChangePass(id,cheView); if (successful) return RedirectToAction("Index"); else return BadRequest("失败"); } return View(); }  [HttpGet] public async Task<IActionResult> ChangeRole(int id) { //本Action调用后,要初始化下拉框的选择 var user = await _manager.FindManagerAsync(id); if (user == null) return NotFound(); #region 绑定类别下拉框 var rolelist = await _managerRoleService.GetManagerRoleAsync(); var roleItems = new List<SelectListItem>() { new SelectListItem(){ Value="0",Text="全部",Selected=true} };  foreach (var role in rolelist) { SelectListItem item = new SelectListItem() { Value = role.Id.ToString(), Text = role.RoleName }; roleItems.Add(item); } //遍历并选中(实现选中下拉框功能) foreach (SelectListItem item in roleItems) { if (item.Value == user.RoleId.ToString()) item.Selected = true; } ViewBag.database = roleItems; #endregion return View(); }  [HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> ChangeRole(int id ,ChangeUserRole user) { //修改用户所属角色 if (string.IsNullOrEmpty(id.ToString())) return NotFound(); #region 取下拉菜单值(RoleId) string strRoleId = Request.Form["ddl_RoleList"]; if (!string.IsNullOrEmpty(strRoleId)) user.RoleId = int.Parse(strRoleId); else user.RoleId = 0; #endregion if (ModelState.IsValid) { try { //ChangeRole方法在ManagerService中,注意其参数 var result = await _manager.ChangeRole(id, user); if (result) return RedirectToAction("Index"); else return BadRequest("编辑失败"); }  catch (Exception ex) { return BadRequest("编辑失败"); } } return View(); }  #region 验证功能  [HttpGet] public async Task<IActionResult> CheckUserName(string UserName) { //result=true,表示有这个用户名,说明验证失败,无法添加 //那么return json 需要返回一个false bool result = await _manager.CheckUserName(UserName); return Json(!result); //返回结果必须是Json格式 }  [HttpGet] public async Task<IActionResult> CheckMobile(string Mobile) { //result=true,表示有这个手机号,说明验证失败,无法添加 //那么return json 需要返回一个false bool result = await _manager.CheckMobile(Mobile); return Json(!result); //返回结果必须是Json格式 }  [HttpGet] public async Task<IActionResult> CheckEmail(string Email) { //result=true,表示有这个邮箱,说明验证失败,无法添加 //那么return json 需要返回一个false bool result = await _manager.CheckEmail(Email); return Json(!result); //返回结果必须是Json格式 }  [HttpGet] public async Task<IActionResult> CheckOldPass(int id,string oldpass) { //result=true,表示有这个旧密码,说明验证失败,无法添加 //那么return json 需要返回一个false string strPass = AESEncryptHelper.Encode(oldpass.Trim(), RjWebKeys.AesEncryptKeys); //对密码加密; bool result = await _manager.CheckOldPass(id,strPass); return Json(result); //返回结果必须是Json格式 } #endregion }

 

2.用户分配角色

分配角色在ChangeRole视图页面完成,注意阅读其对应代码;