分享web开发知识

注册/登录|最近发布|今日推荐

主页 IT知识网页技术软件开发前端开发代码编程运营维护技术分享教程案例
当前位置:首页 > 技术分享

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

发布时间:2023-09-06 02:27责任编辑:赖小花关键词:.NET

 前言:

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

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

  微软文档: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 ConfirmedPassword { 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<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" onkeydown="" 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 ??}

   这里请自行输入自己的 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 并发送邮件:

 ???????[HttpPost] ???????[ValidateAntiForgeryToken] ???????public async Task<IActionResult> 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<bool> 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($" ?请点击<a href=\"{url}\">此处</a>重置您的密码"); ???????????????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 ConfirmPassword { get; set; }17 ????}

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

1 ????????public IActionResult ResetPassword(string userId,string code)2 ????????{3 ????????????ResetPasswordModel model=new ResetPasswordModel()4 ????????????{5 ????????????????UserId = userId,6 ????????????????Code = code7 ????????????};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 对象:

 1 ????????[ValidateAntiForgeryToken] 2 ????????[HttpPost] 3 ????????public async Task<IActionResult> 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 ????<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>

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

原文地址:https://www.cnblogs.com/gokoururi/p/10153040.html

知识推荐

我的编程学习网——分享web前端后端开发技术知识。 垃圾信息处理邮箱 tousu563@163.com 网站地图
icp备案号 闽ICP备2023006418号-8 不良信息举报平台 互联网安全管理备案 Copyright 2023 www.wodecom.cn All Rights Reserved