首页 > 编程语言 >ASP.NET Core Identity 系列之二

ASP.NET Core Identity 系列之二

时间:2023-06-19 22:56:18浏览次数:55  
标签:Core ASP 用户 userManager public user NET Email appUser

转自:https://mp.weixin.qq.com/s?__biz=MzA3NDM1MzIyMQ==&mid=2247486148&idx=1&sn=dae55b414e123c6718e470c21c8c8c21
桂迹,微信公众号

这节我们主要演示在ASP.NET Core Identity中创建、修改、删除、查询用户

1. ASP.NET Core Identity UserManager 类

UserManager类位于Microsoft.AspNetCore.Identity命名空间中,我们可以使用该类管理数据库中的用户。我们使用泛型版本的 UserManager<T> 类来实现基本的CRUD操作

下面描述泛型版本类常用成员

方法名称 描述
FindByIdAsync(id) 根据指定的ID查询用户对象
CreateAsync(user,password) 注册一个新的用户
UpdateAsync(user) 修改一个已经存在的用户
DeleteAsync(user) 移除一个指定的用户
AddToRoleAsync(user, name) 给用户添加角色
RemoveFromRoleAsync(user, name) 从角色中移除用户
GetRolesAsync(user) 获取角色名字列表
IsInRoleAsync(user, name) 返回一个标志,表示是否是给与角色名字的成员

在前面的例子中,我们创建了EF Core DBContext类和AppUser类,AppUser表示数据库中的用户

表格中显示AppUser类常用的属性

名称 描述
Id 包含用户唯一ID
UserName 包含用户名称
Email 包含用户Email

2. 创建Identity 用户

我们在Models文件创建一个User.cs的类,该类中有三个公共属性Name,Email & Password,类型都是字符串,我们给每个属性都加上 [Required] 特性。

为了验证正确的Email输入,在Email属性上加上正则表达式验证(RegularExpression)

 public class User
 {
    [Required]
    [DisplayName("用户名")]
    public string Name { get; set; } = null!;
    [Required]
    [DisplayName("邮箱")]
    [RegularExpression("^[a-zA-Z0-9_\\.-]+@([a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,6}$", ErrorMessage = "电子邮件格式不正确")]
    public string Email { get; set; } = null!;
    [Required]
    [DataType(DataType.Password)]
    [DisplayName("密码")]
    public string Password { get; set; } = null!;
}

在Controllers文件夹下新增一个新的AdminController类,在该控制器中创建CURD方法,AdminController如下:

public class AdminController : Controller
{
    private UserManager<AppUser> _userManager;
    public AdminController(UserManager<AppUser> userManager)
    {
       _userManager = userManager;
    }
    public IActionResult Index()
    {
       return View();
    }
}

在AdminController的构造函数中引用UserManager类,接下来我们添加Create方法来创建新用户,如下代码:

public ViewResult Create() => View();

[HttpPost]
public async Task<IActionResult> Create(User user)
{
    if (ModelState.IsValid)
    {
        AppUser appUser = new AppUser
        {
            UserName = user.Name,
            Email = user.Email,
        };
        var identityResult = await _userManager.CreateAsync(appUser, user.Password);
        if (identityResult.Succeeded)
        {
            return RedirectToAction("Index", "Admin");
        }
        else
        {
            foreach (IdentityError error in identityResult.Errors)
            {
                ModelState.AddModelError("", error.Description);
            }
        }
    }
    return View(user);
}

Post版本的Create方法创建一个新的Identity用户,该方法的参数获取一个User类并检查模型绑定是否成功。如果模型绑定成功创建一个AppUser类的实例并且将UserName和Email赋值给该对象

AppUser appUser = new AppUser
{
   UserName = user.UserName,
   Email = user.Email,
};

在这个之后,我们通过调用UserManager类的CreateAsync创建一个用户,CreateAsync方法有两个参数,一个是AppUser对象类,第二个是Password。该方法返回IdentityResult类型

var identityResult = await _userManager.CreateAsync(appUser, user.Password);

IdentityResult 类包含下面属性:

Succeeded – 如果这个属性返回true,则表示该用户创建成功
Errors – 这个属性是IEnumerable<IdentityError>类型,表示在创建用户过程中发生的错误信息,IdentityError有个Description属性表示错误的详细信息。

我们可以使用Succeeded属性来判断用户是否创建成功。如果成功则跳转到Index视图,否则我们将所有的错误添加到ModelState中,代码如下:

if (identityResult.Succeeded)
{
    return RedirectToAction("Index", "Admin");
}
else
{
   foreach (IdentityError error in identityResult.Errors)
   {
      ModelState.AddModelError("", error.Description);
   }
}

最后一行我们将user返回到Create视图 - return View(user),将错误信息显示在浏览器提示用户改正
接下来我们在视图文件Views -> Admin文件夹下创建一个Create.cshtml,代码如下:

@model User
@{
    ViewData["Title"] = "新增用户";
}
<div asp-validation-summary="All" class="text-danger"></div>
<form class="form-horizontal" role="form" method="post">
    <div class="mb-3 row">
        <label asp-for="Name" class="col-sm-1 control-label"></label>
        <div class="col-sm-11">
            <input asp-for="Name" class="form-control" />
        </div>
    </div>
    <div class="mb-3 row">
        <label asp-for="Email" class="col-sm-1 control-label"></label>
        <div class="col-sm-11">
            <input asp-for="Email" class="form-control" />
        </div>
    </div>
    <div class="mb-3 row">
        <label asp-for="Password" class="col-sm-1 control-label"></label>
        <div class="col-sm-11">
            <input asp-for="Password" class="form-control" />
        </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>

用户可以通过该表单将 用户名&邮件&密码 提交 AdminController 的 Create 方法,从而保存到数据库中

接下来我们创建一个新的用户,运行应用程序在浏览器中输入下面地址:https://localhost:7296/Admin/Create
image

输入下面信息并点击确认:

请注意,Identity要求密码长度至少为6个字符,并且必须至少包含一个非字母数字、一个小写和一个大写字符

每次创建完用户之后都会跳转到Index 视图,该视图展示了数据库中所有的用户信息。接下来我们创建显示用户列表的页面

不能创建一个相同的用户,如果你创建相同的用户你会发现获取错误提示 User name ‘xxx’ is already taken

我们创建所有Identity用户存储到AspNetUsers表,打开这个表发现用户记录已经被创建。出于安全目的,ASP.NET Core Identity将我们密码进行加密存储,并不是存储的平面文本:
image

3. 查询Identity所有用户

我们使用UserManager类能从数据库中获取所有的Identity用户。Users属性将返回一个IEnumerable对象,修改一下AdminCont
roller的Index方法:

public class AdminController : Controller
{
    private UserManager<AppUser> _userManager;
    public AdminController(UserManager<AppUser> userManager)
    {
        _userManager = userManager;
    }
    public IActionResult Index()
    {
        return View(_userManager.Users);
    }
}

我们将所有的用户以IEnumerable形式返回到View。

现在我们在Views -> Admin目录下创建Index.cshtml视图,显示用户的详细信息。

@model IEnumerable<AppUser>
@{
    ViewData["Title"] = "用户列表";
}
<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 table-bordered align-middle">
                <thead>
                    <tr>
                        <th>编号</th>
                        <th>名称</th>
                        <th>邮件</th>
                    </tr>
                </thead>
                <tbody>
                    @foreach (AppUser user in Model)
                    {
                        <tr>
                            <td>@user.Id</td>
                            <td>@user.UserName</td>
                            <td>@user.Email</td>
                        </tr>
                    }
                </tbody>
            </table>
        </div>
    </div>
</div>

在这个View中接受一个 IEnumerable<AppUser> 的模型类,使用foreach循环该类将所有用户的信息显示在表格内

我们测试一下该功能,在浏览其中输入https://localhost:7296/Admin ,将调用Index方法。下面图中显示了在数据库中的所有的用户记录:
image

4. 更新用户记录

现在对用户记录做更新,在Index.cshtml页面表格中添加Update列,在这一列中添加锚定链接到Update方法

@model IEnumerable<AppUser>
@{
    ViewData["Title"] = "用户列表";
}
<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 table-bordered align-middle">
                <thead>
                    <tr>
                        <th>编号</th>
                        <th>名称</th>
                        <th>邮件</th>
                        <th>编辑</th>
                    </tr>
                </thead>
                <tbody>
                    @foreach (AppUser user in Model)
                    {
                        <tr>
                            <td>@user.Id</td>
                            <td>@user.UserName</td>
                            <td>@user.Email</td>
                            <td>
                                <a class="btn btn-primary btn-sm" asp-action="Update" asp-route-id="@user.Id">编辑</a>
                            </td>
                        </tr>
                    }
                </tbody>
            </table>
        </div>
    </div>
</div>

我们需要在AdminController的构造函数添加IPasswordHasher依赖。使用它来获取用户密码的哈希值。出于安全目ASP.NET Core Identity存储用户密码使用的是用户密码的哈希值,而不是平面文本。因此黑客获取到用户信息,是无法登录

我们修改一下 AdminController:

public class AdminController : Controller
    {
        private UserManager<AppUser> _userManager;
        private IPasswordHasher<AppUser> _passwordHasher;
        public AdminController(UserManager<AppUser> userManager,
                               IPasswordHasher<AppUser> passwordHash)
        {
            _userManager = userManager;
            _passwordHasher = passwordHash;
        }
}

我们在AdminController中添加Update 方法,代码如下:

public async Task<IActionResult> Update(string Id)
{
    var appUser = await _userManager.FindByIdAsync(Id);
    if (appUser != null)
    {
        var updateUserDTO = new UpdateUserDTO();
        updateUserDTO.Id = appUser.Id;
        updateUserDTO.Name = appUser.UserName ?? "";
        updateUserDTO.Email = appUser.Email == null ? "" : appUser.Email;
        return View(updateUserDTO);
    }
    else
    {
        return RedirectToAction("Index");
    }
}

[HttpPost]
public async Task<IActionResult> Update(UpdateUserDTO updateUserDTO)
{
    var appUser = await _userManager.FindByIdAsync(updateUserDTO.Id);
    if (appUser != null)
    {
        if (updateUserDTO.Email != null)
            appUser.Email = updateUserDTO.Email;
        else
            ModelState.AddModelError("", "邮箱不能为空");
        if (updateUserDTO.Name != null)
            appUser.UserName = updateUserDTO.Name;
        else
            ModelState.AddModelError("", "用户名不能为空");
        if (updateUserDTO.Password != null)
            appUser.PasswordHash = _passwordHasher.HashPassword(appUser, updateUserDTO.Password);
        else
            ModelState.AddModelError("", "密码不能为空");
        if (!string.IsNullOrEmpty(updateUserDTO.Email) &&
            !string.IsNullOrEmpty(updateUserDTO.Password))
        {
            var result = await _userManager.UpdateAsync(appUser);
            if (result.Succeeded)
                return RedirectToAction("Index");
            else
            {
                foreach (IdentityError error in result.Errors)
                {
                    ModelState.AddModelError("", error.Description);
                }
            }
        }
    }
    else
        ModelState.AddModelError("", "没有发现该用户");
    return View(updateUserDTO);
}

HTTP GET版本的 Update 方法参数接受一个字符串类型的Id。这个Id表示用户注册时生成的Id,用 UserManagerFindByIdAsync() 方法从 Identity数据库 中查找该用户记录,该方法提供了通过用户Id查找用户信息

var appUser = await _userManager.FindByIdAsync(Id);

每次查找完用户信息后会将用户信息作为实体发送到Update.cshtml 视图。当用户点击提交按钮时会将表单的数据提交到HTTP POST 版本的Update方法,该方法会更新用户数据。首先使用FindByIdAsync()方法获取用户信息:

var appUser = await _userManager.FindByIdAsync(Id);

如果AppUser对象不是null,我们检查email和password是否为null,如果不为null我们更新AppUser类Email和PasswordHash属性值,我们将修改后的AppUser作为参数传递给UserManager类的UpdateAsync方法

if (updateUserDTO.Email != null)
    appUser.Email = updateUserDTO.Email;
else
    ModelState.AddModelError("", "邮箱不能为空");
if (updateUserDTO.Password != null)
    appUser.PasswordHash = _passwordHasher.HashPassword(appUser, updateUserDTO.Password);
else
    ModelState.AddModelError("", "密码不能为空");

注意我们使用HashPassword()方法生成用户密码的哈希值

appUser.PasswordHash = _passwordHasher.HashPassword(appUser, updateUserDTO.Password);

使用UpdateAsync方法更新,这个方法参数是一个AppUser的对象。代码如下:

var result = await _userManager.UpdateAsync(appUser);
if (result.Succeeded)
    return RedirectToAction("Index");
else
     foreach (IdentityError error in result.Errors)
         Errors(error);

如果更新方法没有执行成功,我们需要调用Errors将对应的错误信息添加到ModelState中,并将这些错误信息显示在浏览器上。代码如需:

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

在Views -> Admin文件夹下创建 Update.cshtml 文件:

@model UpdateUserDTO
@{
    ViewData["Title"] = "修改用户";
}
<div asp-validation-summary="All" class="text-danger"></div>
<form class="form" method="post" role="form">
    <div class="row mb-3">
        <label asp-for="Id" class="col-sm-1 control-label"></label>
        <div class="col-sm-11">
            <input asp-for="Id" class="form-control" disabled />
        </div>
    </div>
     <div class="mb-3 row">
        <label asp-for="Name" class="col-sm-1 control-label"></label>
        <div class="col-sm-11">
            <input asp-for="Name" class="form-control" />
        </div>
    </div>
    <div class="row mb-3">
        <label asp-for="Email" class="col-sm-1 form-label"></label>
        <div class="col-sm-11">
            <input asp-for="Email" class="form-control" />
        </div>
    </div>
    <div class="row mb-3">
        <label asp-for="Password" class="col-sm-1 form-label"></label>
        <div class="col-sm-11">
            <input asp-for="Password" class="form-control" />
        </div>
    </div>
    <div class="row mb-3">
        <div class="col-sm-11 offset-sm-1">
            <button class="btn btn-primary">保存</button>
            <button asp-action="index" class="btn btn-secondary">返回</button>
        </div>
    </div>
</form>
public class UpdateUserDTO
{
    [Required]
    [DisplayName("编号")]
    public string Id { get; set; } = null!;
    [Required]
    [DisplayName("用户名")]
    public string Name { get; set; } = null!;
    [Required]
    [DisplayName("邮箱")]
    [RegularExpression("^[a-zA-Z0-9_\\.-]+@([a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,6}$", ErrorMessage = "E-mail is not valid")]
    public string Email { get; set; } = null!;
    [Required]
    [DataType(DataType.Password)]
    [DisplayName("密码")]
    public string Password { get; set; } = null!;
}

我们测试一下更新功能,启动应用程序在浏览器中输入如下地址:https://localhost:7296/Admin ,点击更新按钮会跳转到对应的更新页面,输入邮箱和密码并且点击保存按钮将会更新数据库中的用户记录:
image
image
image

4. 删除用户

最后我们完成删除操作。使用AdminControoler中的Delete方法完成。代码如下:

[HttpPost]
public async Task<IActionResult> Delete(string Id)
{
    var appUser = await _userManager.FindByIdAsync(Id);
    if (appUser != null)
    {
        var result = await _userManager.DeleteAsync(appUser);
        if (result.Succeeded)
            return RedirectToAction("Index");
        else
            Errors(result);
    }
   else
     ModelState.AddModelError("", "没有发现该用户");
   return View("Index", _userManager.Users);
}

删除方法接受一个用户Id并且调用FindByIdAsync()方法查询对应用户记录,获取到相匹配的用户之后使用DeleteAsync方法删除。代码如下:

var result = await _userManager.DeleteAsync(appUser);

如果删除成功,用户会跳转到Index方法,否则会将相应的错误显示到浏览器

我们在Index.cshtml页面添加删除按钮。代码如下:

@model IEnumerable<AppUser>
@{
    ViewData["Title"] = "用户列表";
}
<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 table-bordered align-middle">
                <thead>
                    <tr>
                        <th>编号</th>
                        <th>名称</th>
                        <th>邮件</th>
                        <th>编辑</th>
                        <th>删除</th>
                    </tr>
                </thead>
                <tbody>
                    @foreach (AppUser user in Model)
                    {
                        <tr>
                            <td>@user.Id</td>
                            <td>@user.UserName</td>
                            <td>@user.Email</td>
                            <td>
                                <a class="btn btn-primary btn-sm" asp-action="Update" asp-route-id="@user.Id">编辑</a>
                            </td>
                            <td>
                                <form method="post" asp-action="Delete" asp-route-id="@user.Id" role="form">
                                    <button type="submit" class="btn btn-danger btn-sm">删除</button>
                                </form>
                            </td>
                        </tr>
                    }
                </tbody>
            </table>
        </div>
    </div>
</div>

运行应用程序,在浏览器中输入https://localhost:7296/admin 地址,我们看到所有的Identity User记录旁边有都一个删除按钮,当我们点击删除按钮时,会将用户从数据库中删除
image
image

5. 总结

这节我们主要介绍了在ASP.NET Core Identity中新增、查询、修改、删除用户,在下一节我们将针对用户名、邮箱、密码创建策略,以及客户自定义规则
源代码地址:
https://github.com/bingbing-gui/Asp.Net-Core-Skill/tree/master/AspNetCore.Identity/Identity

标签:Core,ASP,用户,userManager,public,user,NET,Email,appUser
From: https://www.cnblogs.com/Ceri/p/17492441.html

相关文章

  • ASP.NET Core MVC 从入门到精通之日志管理
    随着技术的发展,ASP.NETCoreMVC也推出了好长时间,经过不断的版本更新迭代,已经越来越完善,本系列文章主要讲解ASP.NETCoreMVC开发B/S系统过程中所涉及到的相关内容,适用于初学者,在校毕业生,或其他想从事ASP.NETCoreMVC系统开发的人员。经过前几篇文章的讲解,初步了解ASP.NETCore......
  • ASP.NET Core Identity 系列之一
    转自:https://mp.weixin.qq.com/s?__biz=MzA3NDM1MzIyMQ==&mid=2247486141&idx=1&sn=f77635080994c6295cb801e846427a15桂迹,微信公众号https://mp.weixin.qq.com/s?__biz=MzA3NDM1MzIyMQ==&mid=2247486318&idx=1&sn=1f06f6de690ba6df16ed812de9588709ASP.N......
  • Nashorn引擎导致metaspace oom
       从报错内容很清楚是Metaspace区域oom了大部分情况下,程序运行中不会出现过多的类加载数量的变动,先导入dump文件检查是否有异常的classLoader或者有异常动态生成的class发现了下面这个classLoader数量异常,项目中用到nashorn这块js引擎来做动态js脚本执行通过查阅nas......
  • calico 网络流量 过程 分析 apt-get install telnet
    1.caliconode容器在kubernetes中以DaemonSet的方式运行,容器的网络模式为hostNetwor,与host共享网络栈,拥有相同的Ip和hostname 2.查看某个pod:[root@bserver40~]#kubectlgetpods-owide-nkube-system|grep-itillertiller-deploy-5dfffddb8d-n4vp6 1/1    Runn......
  • Kubernetes哪一点最打动你?或者,它发布过的哪一项特性让你认为最厉害?
    kubernates打动我的地方应该是他解决了docker的一个痛点,各个docker之间的通信以及集成管理。因为这跟微服务很像,微服务之间也是需要通信和统一管理。知识总是相同的,在这里就体现出来了。用一个例子来演示会更加清晰......
  • kubernetes 生命周期问题分析
    1.Failed --pod里至少一个容器以非0code退出,说明应用有问题,需要debug应用容器2.pending--说明API对象已经被创建和保存在etcd数据库里,但是创建过程出了问题,可能是imagepull出问题,也可能是调度出了问题3.Unknow--说明pod的状态不能持续地被Kubelet发送给kubeapi,这很可能是......
  • kubernetes 资源请求和限制
    1.spec:containers:-name:exampleresources:requests:cpu:100mmemory:64Milimits:cpu:200mmemory:128Mi   例如,一个带有3个容器的pod,每个容器请求0......
  • kubernetes pvc pv 坑
    这里遇到一个问题,开始建立的pv死活claim为空,查看pv以及pvc的配置发现并没有任何名称上的关联,继续研究,发现纯粹是通过storage大小进行匹配的,之前因为照抄书本,一个是5G,一个是8G所以就无法匹配了,修改后成功。用一个例子来演示会更加清晰......
  • .Net Core Mvc 2. VS2022 + Core6.0 + Mvc 添加视图,传递数据
    添加视图找到控制器HelloWorldController的叫Index的Action在上面右键,选择添加视图,选择空,确认后会在Views文件夹下新建一个HelloWorld文件夹,并生成Index.cshtml文件。直接运行 可以看到Index视图有默认的头部内容和底部内容(太长了没截取),这个效果是在Views\Shared文件夹下的_......
  • Kubernetes——存储
    目录Volume分类示例1:通过emptyDir共享数据示例2:使用HostPath挂载宿主机文件示例3:挂载NFS至容器PersistentVolume基于NFS的PV基于HostPath的PV基于CephRBD的PVPersistentVolumeClaim创建PVC使用PVC动态存储StorageClass定义StorageClass整合StorageClass和CephRBDCSI容器存储接口......