前言:

本系列文章主要为我之前所学知识的一次微小的实践,以我学校图书馆管理系统为雏形所作。

本系列文章主要参考资料:

微软文档:https://docs.microsoft.com/zh-cn/aspnet/core/getting-started/?view=aspnetcore-2.1&tabs=windows

《Pro ASP.NET MVC 5》、《锋利的 jQuery》

此系列皆使用 VS2017+C# 作为开发环境。如果有什么问题或者意见欢迎在留言区进行留言。

项目 github 地址:https://github.com/NanaseRuri/LibraryDemo

  本章内容:Identity 修改密码和找回密码、c# SMTP 的使用、配置文件的使用

一、添加密码修改功能

  首先创建对应的视图模型:

  其中 16 行的 [Compare] 特性构造函数参数为需进行对比的属性,此处用于确认修改后的密码。  

 1     public class ModifyModel
 2     {
 3         [UIHint("password")]
 4         [Display(Name = "原密码")]
 5         [Required]
 6         public string OriginalPassword { get; set; }
 7
 8         [Required]
 9         [Display(Name = "新密码")]
10         [UIHint("password")]
11         public string ModifiedPassword { get; set; }
12
13         [Required]
14         [Display(Name = "确认密码")]
15         [UIHint("password")]
16         [Compare("ModifiedPassword", ErrorMessage = "两次密码不匹配")]
17         public string ConfirmedPassword { get; set; }
18     }

  利用 Identity 框架中 UserManager 对象的 ChangePasswordAsync 方法用来修改密码,该方法返回一个 IdentityResult 对象,可通过其 Succeeded 属性查看操作是否成功。在此修改成功后调用 _signInManager.SignOutAsync() 方法来清除当前 Cookie。

  定义用于修改密码的动作方法和视图:

 1         public IActionResult ModifyPassword()
 2         {
 3             ModifyModel model=new ModifyModel();
 4             return View(model);
 5         }
 6
 7         [HttpPost]
 8         [ValidateAntiForgeryToken]
 9         public async Task<IActionResult> ModifyPassword(ModifyModel model)
10         {
11             if (ModelState.IsValid)
12             {
13                 string username = HttpContext.User.Identity.Name;
14                 var student = _userManager.Users.FirstOrDefault(s => s.UserName == username);
15                 var result =
16                     await _userManager.ChangePasswordAsync(student, model.OriginalPassword, model.ModifiedPassword);
17                 if (result.Succeeded)
18                 {
19                     await _signInManager.SignOutAsync();
20                     return View("ModifySuccess");
21                 }
22                 ModelState.AddModelError("","原密码输入错误");
23             }
24             return View(model);
25         }

  ModifyPassword 视图,添加用以表示是否显示密码的复选框,并使用 jQuery 和 JS 添加相应的事件。将<script></script>标签统一放在 @section Scripts 以方便地使用布局:

 1     @model ModifyModel
 2
 3     @{
 4         ViewData["Title"] = "ModifyPassword";
 5     }
 6
 7     @section Scripts{
 8         <script>
 9             $(document).ready(function() {
10                 var $btn = $("#showPas");
11                 var btn = $btn.get(0);
12                 $btn.click(function() {
13                     if (btn.checked) {
14                         $(".pass").attr("type", "");
15                     } else {
16                         $(".pass").attr("type", "password");
17                     }
18                 });
19             })
20         </script>
21     }
22
23
24     <h2>修改密码</h2>
25
26     <div class="text-danger" asp-validation-summary="All"></div>
27     <form asp-action="ModifyPassword" method="post">
28         <div class="form-group">
29             <label asp-for="OriginalPassword"></label>
30             <input asp-for="OriginalPassword" class="pass"/>
31         </div>
32         <div class="form-group">
33             <label asp-for="ModifiedPassword"></label>
34             <input asp-for="ModifiedPassword" id="modifiedPassword" class="pass"/>
35         </div>
36         <div class="form-group">
37             <label asp-for="ConfirmedPassword"></label>
38             <input asp-for="ConfirmedPassword" id="confirmedPassword" οnkeydοwn="" class="pass"/>
39         </div>
40         <div class="form-group">
41             <label>显示密码 </label><input style="margin-left: 10px" type="checkbox" id="showPas"/>
42         </div>
43         <input type="submit"/>
44         <input type="reset"/>
45     </form>

  随便建的 ModifySuccess 视图:

1     @{
2         ViewData["Title"] = "修改成功";
3     }
4
5     <h2>修改成功</h2>
6
7     <h4><a asp-action="Login">请重新登录</a></h4>

  然后修改 AccountInfo 视图以添加对应的修改密码的按钮:

 1     @model Dictionary<string, object>
 2     @{
 3         ViewData["Title"] = "AccountInfo";
 4     }
 5     <h2>账户信息</h2>
 6     <ul>
 7         @foreach (var info in Model)
 8         {
 9             <li>@info.Key: @Model[info.Key]</li>
10         }
11     </ul>
12     <br />
13     <a class="btn btn-danger" asp-action="Logout">登出</a>
14     <a class="btn btn-primary" asp-action="ModifyPassword">修改密码</a>

Cookie 被清除:

 二、重置密码

  在 Identity 框架中, UserManager 提供了 GeneratePasswordResetTokenAsync 以及 ResetPasswordAsync 方法用以重置密码。

  现实生活中,一般通过邮件发送重置连接来重置密码,为日后更方便地配置,在此创建 Mail.json

 1 {
 2     "Mail": {
 3       "MailFromAddress": "",
 4       "UseSsl": "false",
 5       "Username": "",
 6       "Password": "",
 7       "ServerPort": "25",
 8       "ServerName": "smtp.163.com",
 9       "UseDefaultCredentials": "true"
10     }
11   }

   各大邮箱运营商拥有自己的 SMTP 服务器,需要对应邮箱的请自行百度。这里仅展示 163 邮箱,这里请自行输入自己的 163 账号和密码。

  然后创建一个类用来配置发送邮件的相关信息:

 1     public class EmailSender
 2     {
 3         IConfiguration emailConfig = new ConfigurationBuilder().AddJsonFile("Mail.json").Build().GetSection("Mail");
 4         public SmtpClient SmtpClient=new SmtpClient();
 5
 6         public EmailSender()
 7         {
 8             SmtpClient.EnableSsl = Boolean.Parse(emailConfig["UseSsl"]);
 9             SmtpClient.UseDefaultCredentials = bool.Parse(emailConfig["UseDefaultCredentials"]);
10             SmtpClient.Credentials = new NetworkCredential(emailConfig["Username"], emailConfig["Password"]);
11             SmtpClient.Port = Int32.Parse(emailConfig["ServerPort"]);
12             SmtpClient.Host = emailConfig["ServerName"];
13             SmtpClient.DeliveryMethod = SmtpDeliveryMethod.Network;
14         }
15     }

  该类定义了一个读取配置的字段,以及一个用来发送邮件的 SmtpClient 属性。

  此处第三行将会从 bin 文件夹中读取 Mail.json 文件中的 Mail 节点,为使 ConfigurationBuilder 能够读取到 bin 文件夹的文件,需要将 Mail.json 设置为复制到输出目录中:

  然后该类将在构造函数对 SmtpClient 进行相应的配置。注意需要在为 SmtpClient 的 Credentials 属性赋值前为 UseDefaultCredentials 赋值,否则 Credentials 将被赋值为空值而出 Bug。

  为使整个网页应用在整个生命期内使用的是同一个 SmtpClient 实例,在 ConfigureServices 中进行配置:  

1     services.AddSingleton<EmailSender>();

  创建用于确定找回途径的模型:

 1     public enum RetrieveType
 2     {
 3         UserName,
 4         Email
 5     }
 6
 7     public class RetrieveModel
 8     {
 9         [Required]
10         public RetrieveType RetrieveWay { get;set; }
11         [Required]
12         public string Account { get; set; }
13     }

  定义一个 PasswordRetrieverController 专门用以处理找回密码的逻辑,Retrieve 方法创建接收用户信息输入的视图:

 1     public class PasswordRetrieverController : Controller
 2     {
 3         private UserManager<Student> _userManager;
 4         public EmailSender _emailSender;
 5
 6         public PasswordRetrieverController(UserManager<Student> studentManager, EmailSender emailSender)
 7         {
 8             _userManager = studentManager;
 9             _emailSender = emailSender;
10         }
11
12         public IActionResult Retrieve()
13         {
14             RetrieveModel model = new RetrieveModel();
15             return View(model);
16         }

  Retrieve 视图:

 1     @model RetrieveModel
 2
 3     <h2>找回密码</h2>
 4     <hr/>
 5
 6     <label class="text-danger">@ViewBag.Error</label>
 7
 8     <form asp-action="RetrievePassword" asp-controller="PasswordRetriever" method="post">
 9         <div class="form-group">
10             <input asp-for="Account" class="form-control" placeholder="请输入你的邮箱 / 账号 / 手机号"/>
11         </div>
12         <br/>
13         <div class="form-group">
14             <label>找回方式</label>
15             <select asp-for="RetrieveWay">
16                 <option disabled value="">找回方式: </option>
17                 <LoginType login-type="@Enum.GetNames(typeof(RetrieveType))"></LoginType>
18             </select>
19         </div>
20         <br/>
21         <input class="btn btn-primary" type="submit" value="确认"/>
22         <input class="btn btn-primary" type="reset"/>
23     </form>

  定义用来进行具体逻辑验证的 RetrievePassword 方法,该方法验证用户是否存在,生成用以重置密码的 token 并发送邮件:

 1         [HttpPost]
 2         [ValidateAntiForgeryToken]
 3         public async Task<IActionResult> RetrievePassword(RetrieveModel model)
 4         {
 5             bool sendResult=false;
 6             if (ModelState.IsValid)
 7             {
 8                 Student student = new Student();
 9                 switch (model.RetrieveWay)
10                 {
11                     case RetrieveType.UserName:
12                         student = await _userManager.FindByNameAsync(model.Account);
13                         if (student != null)
14                         {
15                             string code = await _userManager.GeneratePasswordResetTokenAsync(student);
16                             sendResult = await SendEmail(student.Id, code, student.Email);
17                         }
18                         break;
19                     case RetrieveType.Email:
20                         student = await _userManager.FindByEmailAsync(model.Account);
21                         if (student != null)
22                         {
23                             string code = await _userManager.GeneratePasswordResetTokenAsync(student);
24                             sendResult = await SendEmail(student.Id, code, student.Email);
25                         }
26                         break;
27                 }
28                 if (student == null)
29                 {
30                     ViewBag.Error("用户不存在,请重新输入");
31                     return View("Retrieve",model);
32                 }
33             }
34             ViewBag.Message = "已发送邮件至您的邮箱,请注意查收";
35             ViewBag.Failed = "信息发送失败";
36             return View(sendResult);
37         }    

  在 PasswordRetrieverController 中定义用以发送邮件的方法,以 bool 为返回值以判断邮件是否发送成功,此处 MailMessage 处的 from 参数请自行配置:

 1         async Task<bool> SendEmail(string userId, string code, string mailAddress)
 2         {
 3             Student student = await _userManager.FindByIdAsync(userId);
 4             if (student!=null)
 5             {
 6                 string url = Url.Action("ResetPassword","PasswordRetriever",new{userId=userId,code=code}, Url.ActionContext.HttpContext.Request.Scheme);
 7                 StringBuilder sb = new StringBuilder();
 8                 sb.AppendLine($"  请点击<a href=\"{url}\">此处</a>重置您的密码");
 9                 MailMessage message = new MailMessage(from: "xxxx@163.com", to: mailAddress, subject: "重置密码", body: sb.ToString());
10                 message.BodyEncoding=Encoding.UTF8;
11                 message.IsBodyHtml = true;
12                 try
13                 {
14                     _emailSender.SmtpClient.Send(message);
15                 }
16                 catch (Exception e)
17                 {
18                     return false;
19                 }
20
21                 return true;
22             }
23             return false;
24         }

  为 Url.Action 方法指定 protocol 参数以生成完整 url ,否则只会生成相对 url,由于此处为发送邮件,所以需要指定 url 为绝对 Url。

  为使用该 token,创建专门用于重置密码的模型,其中 Code 用来接收 GeneratePasswordResetTokenAsync 生成的 token,UserId 用来传递待重置用户的 Id:

    public class ResetPasswordModel{public string Code { get; set; }public string UserId { get; set; }[Required][Display(Name="密码")][DataType(DataType.Password)]public string Password { get; set; }[Required][Display(Name = "确认密码")][DataType(DataType.Password)][Compare("Password",ErrorMessage = "两次密码不匹配")]public string ConfirmPassword { get; set; }}

  定义用来重置密码的方法 ResetPassword:

1         public IActionResult ResetPassword(string userId,string code)
2         {
3             ResetPasswordModel model=new ResetPasswordModel()
4             {
5                 UserId = userId,
6                 Code = code
7             };
8             return View(model);
9         }

  ResetPassword 视图,此视图将 token 和userId 设置为隐藏字段以在请求中传递:

 1     @model ResetPasswordModel
 2     @{
 3         ViewData["Title"] = "ResetPassword";
 4     }
 5
 6     <h2>重置密码</h2>
 7
 8     <form asp-action="ResetPassword" method="post" asp-antiforgery="true">
 9         <div class="form-group">
10             @Html.HiddenFor(m=>m.Code)
11             @Html.HiddenFor(m=>m.UserId)
12             <label asp-for="Password"></label>
13             <input asp-for="Password"/>
14         </div>
15         <div class="form-group">
16             <label asp-for="ConfirmPassword"></label>
17             <input asp-for="ConfirmPassword"/>
18         </div>
19         <input type="submit"/>
20         <input type="reset"/>
21     </form>

  定义用以具体逻辑验证的 ResetPassword 方法,UserManager<T> 对象的 ResetPasswordAsync 方法接收一个 T类型对象、一个 token 字符串以及密码,返回 IdentityResult 对象:

        [ValidateAntiForgeryToken][HttpPost]public async Task<IActionResult> ResetPassword(ResetPasswordModel model){if (ModelState.IsValid){var user = _userManager.FindByIdAsync(model.UserId);if (user!=null){var result = await _userManager.ResetPasswordAsync(user.Result, model.Code, model.Password);if (result.Succeeded){return RedirectToAction(nameof(ResetSuccess));}}}return View(model);}

  随便定义的用以表示更改成功的 ResetSuccess 方法和视图:

1           public IActionResult ResetSuccess()
2           {
3               return View();
4           }

1     @{
2         ViewData["Title"] = "ResetSuccess";
3     }
4
5     <h2>重置成功</h2>
6
7     <h3>点击<a asp-action="Login" asp-controller="StudentAccount" target="_blank">此处</a>进行登录</h3>

  最后向之前建立的 _LoginParitalView 视图中添加找回密码的按钮:

 1     @model LoginModel
 2
 3     <input type="hidden" name="returnUrl" value="@ViewBag.returnUrl"/>
 4     <div class="form-group">
 5         <label asp-for="Account"></label>
 6         <input asp-for="Account" class="form-control" placeholder="请输入你的账号(学号) / 邮箱 / 手机号"/>
 7     </div>
 8     <div class="form-group">
 9         <label asp-for="Password"></label>
10         <input asp-for="Password" class="form-control" placeholder="请输入你的密码"/>
11     </div>
12     <div class="form-group">
13         <label>登录方式</label>
14         <select asp-for="LoginType">
15             <option disabled value="">登录方式</option>
16             <LoginType login-type="@Enum.GetNames(typeof(LoginType))"></LoginType>
17         </select>
18     </div>
19     <input type="submit" class="btn btn-primary"/>
20     <input type="reset" class="btn btn-primary"/>
21     <a class="btn btn-success" asp-action="Retrieve" asp-controller="PasswordRetriever">找回密码</a>

转载于:https://www.cnblogs.com/gokoururi/p/10380756.html

ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(三)密码修改以及密码重置...相关推荐

  1. 在ASP.NET Core MVC中构建简单 Web Api

    Getting Started 在 ASP.NET Core MVC 框架中,ASP.NET 团队为我们提供了一整套的用于构建一个 Web 中的各种部分所需的套件,那么有些时候我们只需要做一个简单的 ...

  2. 20190318-使用类做一个简单的图书馆管理系统

    要求:使用类的形式做一个图书馆管理系统,实现借书,入库,还书,查书等功能. 设计思路: 第一步:先写一个书的类,来存储图书馆中最重要的组成部分书的信息管理,包括书名,书作者,书的所属类别,书的价格等 ...

  3. ASP.NET Core MVC+EF Core从开发到部署

    笔记本电脑装了双系统(Windows 10和Ubuntu16.04)快半年了,平时有时间就喜欢切换到Ubuntu系统下耍耍Linux,熟悉熟悉Linux命令.Shell脚本以及Linux下的各种应用的 ...

  4. 这本694页的程序员砖头书让你精通ASP.NET Core MVC

    ASP.NET Core MVC是一个来自微软的Web应用程序开发框架,它结合了模型-视图-控制器(MVC)体系结构的有效性和整洁性.敏捷开发的想法和技术,以及.NET平台的最佳部分. 1.1 ASP ...

  5. asp.net core MVC 过滤器之ExceptionFilter过滤器(一)

    简介 异常过滤器,顾名思义,就是当程序发生异常时所使用的过滤器.用于在系统出现未捕获异常时的处理. 实现一个自定义异常过滤器 自定义一个异常过滤器需要实现IExceptionFilter接口 publ ...

  6. 从零开始实现ASP.NET Core MVC的插件式开发(三) - 如何在运行时启用组件

    标题:从零开始实现ASP.NET Core MVC的插件式开发(三) - 如何在运行时启用组件 作者:Lamond Lu 地址:https://www.cnblogs.com/lwqlun/p/112 ...

  7. ASP.NET Core MVC 配置全局路由前缀

    前言 大家好,今天给大家介绍一个 ASP.NET Core MVC 的一个新特性,给全局路由添加统一前缀.严格说其实不算是新特性,不过是Core MVC特有的. 应用背景 不知道大家在做 Web Ap ...

  8. Kubernetes中分布式存储Rook-Ceph的使用:一个ASP.NET Core MVC的案例

    在<Kubernetes中分布式存储Rook-Ceph部署快速演练>文章中,我快速介绍了Kubernetes中分布式存储Rook-Ceph的部署过程,这里介绍如何在部署于Kubernete ...

  9. 关于MVC与三层架构、个人总结网上杂七杂八得出的最终成果、asp.net (core) MVC、JavaWeb的MVC

    阅读本文必须明白的事情 首先需要明白的是不同语言实现的MVC与三层架构对应的层是不一样的!!! 拿.net来说,.net实现MVC与其他语言的MVC具体实现是不同的,asp.net MVC与 MVC ...

最新文章

  1. leetcode算法题--最长等差数列★
  2. css中定义超级链接的样式
  3. 两数相加c++_LeetCode 热题 HOT 100(01,两数相加)
  4. hive-内置函数(常用内置函数汇总)
  5. 让“学生看得明白” 复旦数学教授在无人教室录课程板书
  6. HDU6092——Rikka with Subset 【dp】
  7. Soul递交IPO招股书:腾讯为第一大股东,依然处于亏损状态
  8. 在android中使用opencv,在安卓上使用OpenCV的指南 - kdnuggets
  9. 风格迁移与Gram matrix
  10. python 实现SOM:代码注释与应用示例
  11. 基金的A、B、C、E、H,有意思!
  12. 整理学习之深度可分离卷积
  13. Unable to instantiate application 解决办法
  14. python爬虫之三 —— 淘宝评论
  15. 《Java编程十五讲》第十一讲:脚本
  16. 当深度学习遇见自动文本摘要
  17. Drools——什么是规则
  18. 学习pytorch中归一化transforms.Normalize
  19. 基本数据类型的默认值
  20. 教妹学Java:不可不知的 Unicode 之锟斤拷

热门文章

  1. java 管理系统 注释_员工管理系统--带注释--oracle系统--java项目
  2. python画太极八卦图_用布尔运算绘制一个太极八卦图
  3. 2021 MetaCamp程序设计大赛线上资格赛 7-2 心情故事
  4. Windows由于在创建转储期间出错,创建转储文件失败导致的蓝底白字蓝屏重启,最全细解决方案
  5. JAVA容器_java集合容器之Stack
  6. 循环遍历java属性_java中循环遍历实体类的属性和数据类型以及属性值
  7. html怎么在jupyter编辑,jupyter home jupyter环境变量怎么设定
  8. linux 运行java工程师_java工程师linux命令,这篇文章就够了
  9. python强制跳出while循环_python如何跳出while循环
  10. 取文字_玉镯取不出来了怎么办?教你6种最有效的方法