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

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

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

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

1.用户管理功能

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

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部分包括用户的增、删、改、查功能,还有对应的修改用户角色,修改用户密码。

(3.2)Create视图代码如下

@{ 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视图代码如下

@{ 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视图代码如下(列表页)

@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视图代码

@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视图代码

@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视图页面完成,注意阅读其对应代码;

posted @ 2022-11-19 17:04  荣景智工  阅读(294)  评论(0编辑  收藏  举报