栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

ASP.NET Core 打造一个简单的图书馆管理系统(四)密码修改以及密码重置

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

ASP.NET Core 打造一个简单的图书馆管理系统(四)密码修改以及密码重置

前言:

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

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

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

  《Pro ASP.NET MVC 5》、《Bootstrap 开发精解》、《锋利的 jQuery》

 

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

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

 

 

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

 

一、添加密码修改功能

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

  其中 [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 /confirm/iedPassword { get; set; }
18     }

 

  在 StudentAccountController 中添加 [Authorize] 特性,然后可以去除 StudentAccountController 中方法的 [Authorize] 特性。当方法不需要授权即可访问时添加 [AllowAnonymous] 特性。

1     [Authorize]
2     public class StudentAccountController : Controller

 

  利用 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 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 添加相应的事件。将标签统一放在 @section scripts 以方便地使用布局:

 1     @model ModifyModel
 2 
 3     @{
 4         ViewData["Title"] = "ModifyPassword";
 5     }
 6 
 7     @section scripts{ 
 8         
21     }
22 
23 
24     修改密码
25 
26     
27     

 

   随便建的 ModifySuccess 视图:

1     @{
2         ViewData["Title"] = "修改成功";
3     }
4 
5     修改成功
6 
7     请重新登录

 

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

 1     @model Dictionary
 2     @{
 3         ViewData["Title"] = "AccountInfo";
 4     }
 5     账户信息
 6     
     7         @foreach (var info in Model)  8         {  9             
  • @info.Key: @Model[info.Key]
  • 10         } 11     
12      13     登出 14     修改密码

 

 

 

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   }

   这里请自行输入自己的 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();

 

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

 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 _userManager;
 4         public EmailSender _emailSender;
 5 
 6         public PasswordRetrieverController(UserManager 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     找回密码
 4     
 5   6     @ViewBag.Error  7   8     

 

 

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

        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task RetrievePassword(RetrieveModel model)
        {
            bool sendResult=false;
            if (ModelState.IsValid)
            {
                Student student = new Student();
                switch (model.RetrieveWay)
                {
                    case RetrieveType.UserName:
                        student = await _userManager.FindByNameAsync(model.Account);
                        if (student != null)
                        {
                            string code = await _userManager.GeneratePasswordResetTokenAsync(student);
                            sendResult = await SendEmail(student.Id, code, student.Email);
                        }
                        break;
                    case RetrieveType.Email:
                        student = await _userManager.FindByEmailAsync(model.Account);
                        if (student != null)
                        {
                            string code = await _userManager.GeneratePasswordResetTokenAsync(student);
                            sendResult = await SendEmail(student.Id, code, student.Email);
                        }
                        break;
                }
                if (student == null)
                {
                    ViewBag.Error("用户不存在,请重新输入");
                    return View("Retrieve",model);
                }
            }
            ViewBag.Message = "已发送邮件至您的邮箱,请注意查收";
            ViewBag.Failed = "信息发送失败";
            return View(sendResult);
        }

 

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

        async Task SendEmail(string userId, string code, string mailAddress)
        {
            Student student = await _userManager.FindByIdAsync(userId);
            if (student!=null)
            {
                string url = Url.Action("ResetPassword","PasswordRetriever",new{userId=userId,code=code}, Url.ActionContext.HttpContext.Request.Scheme);
                StringBuilder sb = new StringBuilder();
                sb.AppendLine($"  请点击此处重置您的密码");
                MailMessage message = new MailMessage(from: "xxxx@163.com", to: mailAddress, subject: "重置密码", body: sb.ToString());
                message.BodyEncoding=Encoding.UTF8;
                message.IsBodyHtml = true;
                try
                {
                    _emailSender.SmtpClient.Send(message);
                }
                catch (Exception e)
                {
                    return false;
                }

                return true;
            }
            return false;
        }

  为 Url.Action 方法指定 protocol 参数以生成完整 url ,否则只会生成相对 url。

 

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

 1     public class ResetPasswordModel
 2     {
 3         public string Code { get; set; }
 4 
 5         public string UserId { get; set; }
 6 
 7         [Required]
 8         [Display(Name="密码")]
 9         [DataType(DataType.Password)]
10         public string Password { get; set; }
11 
12         [Required]
13         [Display(Name = "确认密码")]
14         [DataType(DataType.Password)]
15         [Compare("Password",ErrorMessage = "两次密码不匹配")]
16         public string /confirm/iPassword { get; set; }
17     }

 

  定义用来重置密码的方法 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     重置密码
 7 
 8     

 

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

 1         [ValidateAntiForgeryToken]
 2         [HttpPost]
 3         public async Task ResetPassword(ResetPasswordModel model)
 4         {
 5             if (ModelState.IsValid)
 6             {
 7                 var user = _userManager.FindByIdAsync(model.UserId);
 8                 if (user!=null)
 9                 {
10                     var result = await _userManager.ResetPasswordAsync(user.Result, model.Code, model.Password);
11                     if (result.Succeeded)
12                     {
13                         return RedirectToAction(nameof(ResetSuccess));
14                     }
15                 }
16             }
17             return View(model);
18         }

 

  随便定义的 ResetSuccess 方法和视图:

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

1     @{
2         ViewData["Title"] = "ResetSuccess";
3     }
4 
5     重置成功
6 
7     点击此处进行登录

 

  最后向 _LoginParitalView 添加找回密码的按钮:

 1 @model LoginModel
 2 
 3     
 4        
 5         
 6         
 7     
 8        
 9         
10         
11     
12     
13         
14         
15             登录方式
16             
17         
18     
19     
20     
21     找回密码

 

 

原文出处:https://www.cnblogs.com/gokoururi/p/10153040.html  

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/230877.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号