ASP.NET Core 3.1 实际操作摸索学习 (Identity部分) - 2

接上篇: ASP.NET Core 3.1 实际操作摸索学习 (Identity部分)- 1

小应用是考虑由管理员来导入或手工输入来新建账号,然后分配相应权限来浏览不同页面;

所以考虑做些小的修改调整来适用于一个内部小应用; (比较惭愧,还没来得及研究Razor Page ,只好先用会的二把刀MVC上了 )

新建一个 UserAdmin的控制器,就用空的来写吧:

ASP.NET Core 3.1 实际操作摸索学习 (Identity部分) - 2

 右键点击打开的 UserAdminController 的

ASP.NET Core 3.1 实际操作摸索学习 (Identity部分) - 2

 View也用空的来吧: 

 ASP.NET Core 3.1 实际操作摸索学习 (Identity部分) - 2

妖怪的是,怎么没像VS2017 MVC4或MVC5那样,直接在Views目录下建个和控制器相同名字的目录,然后再在里面建好View呢?(目前还没想明白)

ASP.NET Core 3.1 实际操作摸索学习 (Identity部分) - 2

  为了规范点,还是把 Index.cshtml 手动删除,在Views目录下再建个UserAdmin目录,再建个Index的View: 

 ASP.NET Core 3.1 实际操作摸索学习 (Identity部分) - 2 

 我把这个UserAdmin放在 Home和Privacy菜单的中间吧,修改_Layout.cshtml

                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="UserAdmin" asp-action="Index">用户管理</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
                        </li> 

首先,先在UserAdmin/Index页面把所有用户列出,并提供编辑、删除按钮: (Index View代码:) 

@using Microsoft.AspNetCore.Identity
@model IEnumerable<IdentityUser>
@{
    ViewBag.Title = "Index";
}
<div class="panel panel-primary">
    <div class="panel-heading">
        用户列表
    </div>
    <table class="table table-striped">
        <tr><th>Name</th><th>Email</th><th>PhoneNumber</th><th></th></tr>
        @if (Model.Count() == 0)
        {
            <tr><td colspan="4" class="text-center">无用户</td></tr>
        }
        else
        {
            foreach (IdentityUser user in Model)
            {
                <tr>        
                    <td>@user.UserName</td>
                    <td>@user.Email</td>
                    <td>@user.PhoneNumber</td>
                    <td>
                            @Html.ActionLink("密码重置", "ResetPassword", new { id = user.Id },
                                new { @class = "btn btn-warning btn-xs" })
                            @Html.ActionLink("编辑", "Edit", new { id = user.Id },
                                new { @class = "btn btn-primary btn-xs" })
                            @Html.ActionLink("删除", "Delete", new { id = user.Id },
                            new { @class = "btn btn-danger btn-xs" })
                    </td>
                </tr>
            }
        }
    </table>
</div>

UserAdmin控制器代码,先把所有用户传到Index View上显示:

    public class UserAdminController : Controller
    {       private readonly UserManager<IdentityUser> _userManager;

        public UserAdminController(UserManager<IdentityUser> userManager)
        {
            _userManager = userManager;            
        }

        public IActionResult Index()
        {            
            return View(_userManager.Users);
        }
    }

实现结果:

ASP.NET Core 3.1 实际操作摸索学习 (Identity部分) - 2

由于应用是一个内部查询应用,密码复杂度要求不需要那么高,调整为长度4位,其他没要求。。。

在Startup.cs 的ConfigureServices 中加入对于密码策略的调整:

public void ConfigureServices(IServiceCollection services)
{
  services.AddDbContext<ApplicationDbContext>(options =>
  options.UseSqlServer(
  Configuration.GetConnectionString("MyConnection")));
  services.AddDefaultIdentity<IdentityUser>(options =>
  {
    options.SignIn.RequireConfirmedAccount = true;
    options.Password.RequireDigit = false;
    options.Password.RequiredLength = 4;
    options.Password.RequireLowercase = false;
    options.Password.RequireNonAlphanumeric = false;
    options.Password.RequireUppercase = false;
  }).AddEntityFrameworkStores<ApplicationDbContext>();
  services.AddControllersWithViews();
  services.AddRazorPages();
}

 

另外,需要把Register.cshtml.cs 中定义的InputModel中的Password的最小长度调整为4:

            [Required]
            [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 4)]
            [DataType(DataType.Password)]
            [Display(Name = "Password")]
            public string Password { get; set; }

用户管理主页面肯定要考虑认证及角色的问题,不可能让未登录用户就可以打开,也不可能让所有登录用户都可以打开,必须是个管理员才可以打开;

那么考虑增加一个角色管理页面来为用户分配角色;

先添加一个RoleAdmin控制器:

 ASP.NET Core 3.1 实际操作摸索学习 (Identity部分) - 2

 

 再手动在Views目录加一个 RoleAdmin目录,在其中加一个Index的空View:

ASP.NET Core 3.1 实际操作摸索学习 (Identity部分) - 2

 

 修改RoleAdmin控制器:

    public class RoleAdminController : Controller
    {
        private readonly RoleManager<IdentityRole> _roleManager;

        public RoleAdminController(RoleManager<IdentityRole> roleManager)
        {
            _roleManager = roleManager;
        }

        public IActionResult Index()
        {
            return View(_roleManager.Roles);
        }
    }

修改RoleAdmin Index的View:

@using Microsoft.AspNetCore.Identity
@model IEnumerable<IdentityRole>
@{
    ViewBag.Title = "Index";
}
<div class="panel panel-primary">
    <div class="panel-heading">
        角色列表
    </div>
    <table class="table table-striped">
        <tr><th>Name</th><th></th></tr>
        @if (Model.Count() == 0)
        {
            <tr><td colspan="4" class="text-center">无角色</td></tr>
        }
        else
        {
            foreach (IdentityRole role in Model)
            {
                <tr>
                    <td>@role.Name</td>                    
                    <td>                        
                        @Html.ActionLink("编辑", "Edit", new { id = role.Id },
                            new { @class = "btn btn-primary btn-xs" })
                        @Html.ActionLink("删除", "Delete", new { id = role.Id },
                        new { @class = "btn btn-danger btn-xs" })
                    </td>
                </tr>
            }
        }
    </table>
</div>
@Html.ActionLink("新建", "Create","RoleAdmin",null, new { @class = "btn btn-primary btn-xs" })

修改 _Layout视图,加上快捷按钮:

                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="UserAdmin" asp-action="Index">用户管理</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="RoleAdmin" asp-action="Index">角色管理</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
                        </li>

跑起来看到有角色管理这个按钮了:

ASP.NET Core 3.1 实际操作摸索学习 (Identity部分) - 2

 

但是如果直接点击 角色管理,就会出现以下错误:

 ASP.NET Core 3.1 实际操作摸索学习 (Identity部分) - 2

 

 

初步研究,应该是没有在ConfigureServices没有加这个(应该怎么描述? 依赖注入?)

services.AddDefaultIdentity<IdentityUser>(options =>
{
  options.SignIn.RequireConfirmedAccount = true;
  options.Password.RequireDigit = false;
  options.Password.RequiredLength = 4;
  options.Password.RequireLowercase = false;
  options.Password.RequireNonAlphanumeric = false;
  options.Password.RequireUppercase = false;
})
.AddRoles<IdentityRole>()
.AddRoleManager<RoleManager<IdentityRole>>()
.AddEntityFrameworkStores<ApplicationDbContext>();

这下没有问题了,可以打开RoleAdmin页面:

ASP.NET Core 3.1 实际操作摸索学习 (Identity部分) - 2

再为RoleAdmin加上 Create的两个Action:

        public ActionResult Create()
        {
            return View();
        }

        [HttpPost]
        public async Task<ActionResult> Create([Required] string name)
        {
            if (ModelState.IsValid)
            {
                IdentityResult result = await _roleManager.CreateAsync(new IdentityRole(name));
                if (result.Succeeded)
                {
                    return RedirectToAction("Index");
                }
                else
                {
                    AddErrorsFromResult(result);
                }
            }
            return View();
        }

        private void AddErrorsFromResult(IdentityResult result)
        {
            foreach (IdentityError error in result.Errors)
            {
                ModelState.AddModelError("", error.Description);
            }
        }

在RoleAdmin的view目录下,新增一个Create View:

@model string
@{ ViewBag.Title = "新增角色";}
<h2>新增角色</h2>
@Html.ValidationSummary(false)
@using (Html.BeginForm())
{
    <div class="form-group">
        <label>Name</label>
        <input name="name" value="@Model" class="form-control" />
    </div>
    <button type="submit" class="btn btn-primary">新增</button>
    @Html.ActionLink("取消", "Index", "RoleAdmin", null, new { @class = "btn btn-default btn-xs" })
}

跑起来以后:

 ASP.NET Core 3.1 实际操作摸索学习 (Identity部分) - 2

 

新增2个Role :   Admin和NormalUser

ASP.NET Core 3.1 实际操作摸索学习 (Identity部分) - 2

 

 下一步考虑为Role来加减下属用户; 本处参考了:https://www.cnblogs.com/r01cn/p/5180892.html#Ch14_3_6  原ASP.NET Identity的一部分代码,学习了其的操作逻辑;

先增加两个ViewModel

    public class RoleViewModel
    {
        public IdentityRole Role { get; set; }
        public IEnumerable<IdentityUser> Members { get; set; }
        public IEnumerable<IdentityUser> NonMembers { get; set; }        
    }

    public class RoleModificationModel
    {
        [Required]
        public string RoleName { get; set; }
        public string[] IdsToAdd { get; set; }
        public string[] IdsToDelete { get; set; }
    }
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using MyCoreTest1.Models;

namespace MyCoreTest1.Controllers
{
    public class RoleAdminController : Controller
    {
        private readonly RoleManager<IdentityRole> _roleManager;
        private readonly UserManager<IdentityUser> _userManager;

        public RoleAdminController(RoleManager<IdentityRole> roleManager, UserManager<IdentityUser> userManager)
        {
            _roleManager = roleManager;
            _userManager = userManager;
        }
        public async Task<IActionResult> Index()
        {
            List<RoleViewModel> roleviewmodels = new List<RoleViewModel>();
            foreach(IdentityRole role in _roleManager.Roles)
            {
                RoleViewModel newrole = new RoleViewModel();
                newrole.Role = role;
                newrole.Members = await _userManager.GetUsersInRoleAsync(role.Name);                
                roleviewmodels.Add(newrole);
            }
            return View(roleviewmodels);
        }

        public ActionResult Create()
        {
            return View();
        }

        [HttpPost]
        public async Task<ActionResult> Create([Required] string name)
        {
            if (ModelState.IsValid)
            {
                IdentityResult result = await _roleManager.CreateAsync(new IdentityRole(name));
                if (result.Succeeded)
                {
                    return RedirectToAction("Index");
                }
                else
                {
                    AddErrorsFromResult(result);
                }
            }
            return View();
        }

        private void AddErrorsFromResult(IdentityResult result)
        {
            foreach (IdentityError error in result.Errors)
            {
                ModelState.AddModelError("", error.Description);
            }
        }

        public async Task<ActionResult> Edit(string id)
        {
            IdentityRole role = await _roleManager.FindByIdAsync(id);
            
            IEnumerable<IdentityUser> members
                    = await _userManager.GetUsersInRoleAsync(role.Name);

            List<IdentityUser> nonMembers = new List<IdentityUser>();
            foreach (IdentityUser user in _userManager.Users)
            {
                if (!members.Any(x => x.Id.Equals(user.Id)))
                {
                    nonMembers.Add(user);
                }
            }

            return View(new RoleViewModel
            {
                Role = role,
                Members = members,
                NonMembers = nonMembers
            });
        }


        [HttpPost]
        public async Task<ActionResult> Edit(RoleModificationModel model)
        {
            IdentityResult result;
            if (ModelState.IsValid)
            {
                foreach (string userId in model.IdsToAdd ?? new string[] { })
                {
                    var user = await _userManager.FindByIdAsync(userId);

                    result = await _userManager.AddToRoleAsync(user, model.RoleName);
                    if (!result.Succeeded)
                    {
                        return View("Error", result.Errors);
                    }
                }
                foreach (string userId in model.IdsToDelete ?? new string[] { })
                {
                    var user = await _userManager.FindByIdAsync(userId);
                    result = await _userManager.RemoveFromRoleAsync(user,
                       model.RoleName);
                    if (!result.Succeeded)
                    {
                        return View("Error", result.Errors);
                    }
                }
                return RedirectToAction("Index");
            }
            return View("Error", new string[] { "Role Not Found" });
        }

    }
}

RoleAdmin改写成以上,首先是把  RoleManager<IdentityRole> roleManager, UserManager<IdentityUser> userManager  传进来;

再把Index的Action 修改一下,可以传一个带用户列表的View Model到前台;

另外,把Edit 的两个Action 按照参考资料里的写法稍微修改一下,来实现Role下属User 的增加删除;

Index的View改成:

@using Microsoft.AspNetCore.Identity
@model IEnumerable<RoleViewModel>
@{
    ViewBag.Title = "Index";
}
<div class="panel panel-primary">
    <div class="panel-heading">
        角色列表
    </div>
    <table class="table table-striped">
        <tr><th>角色名</th><th>下属用户</th><th></th></tr>
        @if (Model.Count() == 0)
        {
            <tr><td colspan="4" class="text-center">无角色</td></tr>
        }
        else
        {
            foreach (RoleViewModel roleview in Model)
            {
                <tr>
                    <td>@roleview.Role.Name</td>
                    <td>
                        @foreach (IdentityUser user in @roleview.Members)
                        {
                            @user.UserName@Html.Label(";")                            
                        }
                    </td>
                    <td>@Html.ActionLink("编辑", "Edit", new { id = roleview.Role.Id },new { @class = "btn btn-primary btn-xs" })
                        @Html.ActionLink("删除", "Delete", new { id = roleview.Role.Id },new { @class = "btn btn-danger btn-xs" })
                    </td>
                </tr>
            }
        }
    </table>
</div>
@Html.ActionLink("新建", "Create","RoleAdmin",null, new { @class = "btn btn-primary btn-xs" })

Role Edit的View改成:

@using MyCoreTest1.Models
@using Microsoft.AspNetCore.Identity
@model RoleViewModel
@{ ViewBag.Title = "Edit Role";}
@Html.ValidationSummary()
@using (Html.BeginForm())
{
    <input type="hidden" name="roleName" value="@Model.Role.Name" />
    <div class="panel panel-primary">
        <div class="panel-heading">添加用户至角色:@Model.Role.Name</div>
        <table class="table table-striped">
            @if (Model.NonMembers.Count() == 0)
            {
                <tr><td colspan="2">所有用户已添加</td></tr>
            }
            else
            {
                <tr><td>用户名</td><td>添加至角色</td></tr>
                foreach (IdentityUser user in Model.NonMembers)
                {
                    <tr>
                        <td>@user.UserName</td>
                        <td>
                            <input type="checkbox" name="IdsToAdd" value="@user.Id">
                        </td>
                    </tr>
                }
            }
        </table>
    </div>
    <div class="panel panel-primary">
        <div class="panel-heading">从角色:@Model.Role.Name 中移除用户</div>
        <table class="table table-striped">
            @if (Model.Members.Count() == 0)
            {
                <tr><td colspan="2">无下属用户</td></tr>
            }
            else
            {
                <tr><td>用户名</td><td>从角色中移除</td></tr>
                foreach (IdentityUser user in Model.Members)
                {
                    <tr>
                        <td>@user.UserName</td>
                        <td>
                            <input type="checkbox" name="IdsToDelete" value="@user.Id">
                        </td>
                    </tr>
                }
            }
        </table>
    </div>
    <button type="submit" class="btn btn-primary">保存</button>
    @Html.ActionLink("取消", "Index", null, null, new { @class = "btn btn-default" })
}

最后效果:

ASP.NET Core 3.1 实际操作摸索学习 (Identity部分) - 2

 

 ASP.NET Core 3.1 实际操作摸索学习 (Identity部分) - 2

 最后把角色管理页面加上 指定的角色才可以打开:

ASP.NET Core 3.1 实际操作摸索学习 (Identity部分) - 2

 

 这样,非Admin角色的用户就打不开了:   (如果非登录状态,就直接自动跳转登录页面)

ASP.NET Core 3.1 实际操作摸索学习 (Identity部分) - 2

 

只有 Admin角色的用户可以打开 角色管理页面:

ASP.NET Core 3.1 实际操作摸索学习 (Identity部分) - 2

 

下面准备修改 用户管理页面,使得管理员可以 新建、批量导入、重置用户密码等操作; 

 

ASP.NET Core 3.1 实际操作摸索学习 (Identity部分) - 2

上一篇:.NET 基础-3


下一篇:ADO.NET调用存储过程