前言:

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

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

微软文档: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] 特性构造函数参数为需进行对比的属性,此处用于确认修改后的密码。  

     public class ModifyModel
{
[UIHint("password")]
[Display(Name = "原密码")]
[Required]
public string OriginalPassword { get; set; } [Required]
[Display(Name = "新密码")]
[UIHint("password")]
public string ModifiedPassword { get; set; } [Required]
[Display(Name = "确认密码")]
[UIHint("password")]
[Compare("ModifiedPassword", ErrorMessage = "两次密码不匹配")]
public string ConfirmedPassword { get; set; }
}

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

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

         public IActionResult ModifyPassword()
{
ModifyModel model=new ModifyModel();
return View(model);
} [HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ModifyPassword(ModifyModel model)
{
if (ModelState.IsValid)
{
string username = HttpContext.User.Identity.Name;
var student = _userManager.Users.FirstOrDefault(s => s.UserName == username);
var result =
await _userManager.ChangePasswordAsync(student, model.OriginalPassword, model.ModifiedPassword);
if (result.Succeeded)
{
await _signInManager.SignOutAsync();
return View("ModifySuccess");
}
ModelState.AddModelError("","原密码输入错误");
}
return View(model);
}

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

     @model ModifyModel

     @{
ViewData["Title"] = "ModifyPassword";
} @section Scripts{
<script>
$(document).ready(function() {
var $btn = $("#showPas");
var btn = $btn.get();
$btn.click(function() {
if (btn.checked) {
$(".pass").attr("type", "");
} else {
$(".pass").attr("type", "password");
}
});
})
</script>
} <h2>修改密码</h2> <div class="text-danger" asp-validation-summary="All"></div>
<form asp-action="ModifyPassword" method="post">
<div class="form-group">
<label asp-for="OriginalPassword"></label>
<input asp-for="OriginalPassword" class="pass"/>
</div>
<div class="form-group">
<label asp-for="ModifiedPassword"></label>
<input asp-for="ModifiedPassword" id="modifiedPassword" class="pass"/>
</div>
<div class="form-group">
<label asp-for="ConfirmedPassword"></label>
<input asp-for="ConfirmedPassword" id="confirmedPassword" onkeydown="" class="pass"/>
</div>
<div class="form-group">
<label>显示密码 </label><input style="margin-left: 10px" type="checkbox" id="showPas"/>
</div>
<input type="submit"/>
<input type="reset"/>
</form>

  随便建的 ModifySuccess 视图:

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

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

     @model Dictionary<string, object>
@{
ViewData["Title"] = "AccountInfo";
}
<h2>账户信息</h2>
<ul>
@foreach (var info in Model)
{
<li>@info.Key: @Model[info.Key]</li>
}
</ul>
<br />
<a class="btn btn-danger" asp-action="Logout">登出</a>
<a class="btn btn-primary" asp-action="ModifyPassword">修改密码</a>

Cookie 被清除:

 二、重置密码

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

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

 {
"Mail": {
"MailFromAddress": "",
"UseSsl": "false",
"Username": "",
"Password": "",
"ServerPort": "",
"ServerName": "smtp.163.com",
"UseDefaultCredentials": "true"
}
}

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

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

     public class EmailSender
{
IConfiguration emailConfig = new ConfigurationBuilder().AddJsonFile("Mail.json").Build().GetSection("Mail");
public SmtpClient SmtpClient=new SmtpClient(); public EmailSender()
{
SmtpClient.EnableSsl = Boolean.Parse(emailConfig["UseSsl"]);
SmtpClient.UseDefaultCredentials = bool.Parse(emailConfig["UseDefaultCredentials"]);
SmtpClient.Credentials = new NetworkCredential(emailConfig["Username"], emailConfig["Password"]);
SmtpClient.Port = Int32.Parse(emailConfig["ServerPort"]);
SmtpClient.Host = emailConfig["ServerName"];
SmtpClient.DeliveryMethod = SmtpDeliveryMethod.Network;
}
}

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

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

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

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

     services.AddSingleton<EmailSender>();

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

     public enum RetrieveType
{
UserName,
Email
} public class RetrieveModel
{
[Required]
public RetrieveType RetrieveWay { get;set; }
[Required]
public string Account { get; set; }
}

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

     public class PasswordRetrieverController : Controller
{
private UserManager<Student> _userManager;
public EmailSender _emailSender; public PasswordRetrieverController(UserManager<Student> studentManager, EmailSender emailSender)
{
_userManager = studentManager;
_emailSender = emailSender;
} public IActionResult Retrieve()
{
RetrieveModel model = new RetrieveModel();
return View(model);
}

  Retrieve 视图:

     @model RetrieveModel

     <h2>找回密码</h2>
<hr/> <label class="text-danger">@ViewBag.Error</label> <form asp-action="RetrievePassword" asp-controller="PasswordRetriever" method="post">
<div class="form-group">
<input asp-for="Account" class="form-control" placeholder="请输入你的邮箱 / 账号 / 手机号"/>
</div>
<br/>
<div class="form-group">
<label>找回方式</label>
<select asp-for="RetrieveWay">
<option disabled value="">找回方式: </option>
<LoginType login-type="@Enum.GetNames(typeof(RetrieveType))"></LoginType>
</select>
</div>
<br/>
<input class="btn btn-primary" type="submit" value="确认"/>
<input class="btn btn-primary" type="reset"/>
</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,由于此处为发送邮件,所以需要指定 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:

         public IActionResult ResetPassword(string userId,string code)
{
ResetPasswordModel model=new ResetPasswordModel()
{
UserId = userId,
Code = code
};
return View(model);
}

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

     @model ResetPasswordModel
@{
ViewData["Title"] = "ResetPassword";
} <h2>重置密码</h2> <form asp-action="ResetPassword" method="post" asp-antiforgery="true">
<div class="form-group">
@Html.HiddenFor(m=>m.Code)
@Html.HiddenFor(m=>m.UserId)
<label asp-for="Password"></label>
<input asp-for="Password"/>
</div>
<div class="form-group">
<label asp-for="ConfirmPassword"></label>
<input asp-for="ConfirmPassword"/>
</div>
<input type="submit"/>
<input type="reset"/>
</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 方法和视图:

           public IActionResult ResetSuccess()
{
return View();
}
     @{
ViewData["Title"] = "ResetSuccess";
} <h2>重置成功</h2> <h3>点击<a asp-action="Login" asp-controller="StudentAccount" target="_blank">此处</a>进行登录</h3>

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

     @model LoginModel

     <input type="hidden" name="returnUrl" value="@ViewBag.returnUrl"/>
<div class="form-group">
<label asp-for="Account"></label>
<input asp-for="Account" class="form-control" placeholder="请输入你的账号(学号) / 邮箱 / 手机号"/>
</div>
<div class="form-group">
<label asp-for="Password"></label>
<input asp-for="Password" class="form-control" placeholder="请输入你的密码"/>
</div>
<div class="form-group">
<label>登录方式</label>
<select asp-for="LoginType">
<option disabled value="">登录方式</option>
<LoginType login-type="@Enum.GetNames(typeof(LoginType))"></LoginType>
</select>
</div>
<input type="submit" class="btn btn-primary"/>
<input type="reset" class="btn btn-primary"/>
<a class="btn btn-success" asp-action="Retrieve" asp-controller="PasswordRetriever">找回密码</a>

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

  1. ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(二)数据库初始化、基本登录页面以及授权逻辑的建立

    前言: 本系列文章主要为我之前所学知识的一次微小的实践,以我学校图书馆管理系统为雏形所作. 本系列文章主要参考资料: 微软文档:https://docs.microsoft.com/zh-cn/asp ...

  2. ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(七) 学生信息增删

    前言: 本系列文章主要为我之前所学知识的一次微小的实践,以我学校图书馆管理系统为雏形所作. 本系列文章主要参考资料: 微软文档:https://docs.microsoft.com/zh-cn/asp ...

  3. ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(六)学生借阅/预约/查询书籍事务

    前言: 本系列文章主要为我之前所学知识的一次微小的实践,以我学校图书馆管理系统为雏形所作. 本系列文章主要参考资料: 微软文档:https://docs.microsoft.com/zh-cn/asp ...

  4. ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(五)外借/阅览图书信息的增删改查

    前言: 本系列文章主要为我之前所学知识的一次微小的实践,以我学校图书馆管理系统为雏形所作. 本系列文章主要参考资料: 微软文档:https://docs.microsoft.com/zh-cn/asp ...

  5. ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(四)图书信息的增删改查

    前言: 本系列文章主要为我之前所学知识的一次微小的实践,以我学校图书馆管理系统为雏形所作. 本系列文章主要参考资料: 微软文档:https://docs.microsoft.com/zh-cn/asp ...

  6. ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(一) 基本模型以及数据库的建立

    前言: 本系列文章主要为我之前所学知识的一次微小的实践,以我学校图书馆管理系统为雏形所作. 本系列文章主要参考资料: 微软文档:https://docs.microsoft.com/zh-cn/asp ...

  7. 002.Create a web API with ASP.NET Core MVC and Visual Studio for Windows -- 【在windows上用vs与asp.net core mvc 创建一个 web api 程序】

    Create a web API with ASP.NET Core MVC and Visual Studio for Windows 在windows上用vs与asp.net core mvc 创 ...

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

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

  9. Pro ASP.NET Core MVC 第6版 第二章(前半章)

    目录 第二章 第一个MVC 应用程序 学习一个软件开发框架的最好方法是跳进他的内部并使用它.在本章,你将用ASP.NET Core MVC创建一个简单的数据登录应用.我将它一步一步地展示,以便你能看清 ...

随机推荐

  1. flex里InputText不能输入中文

    最近做项目都没做任何的更新,今天突然遇到在flex里的InputText无法进行中文输入,晚上查找了下资料,很多原因说是flashplayer的一个BUG. 在网上找到两种解决办法: 1.会出现这种情 ...

  2. poj - 3254 Corn Fields (状态压缩dp入门)

    http://poj.org/problem?id=3254 参考:http://blog.csdn.net/accry/article/details/6607703 农夫想在m*n的土地上种玉米, ...

  3. 【webstorm 系列之一】快捷键很好用啊

    书签 bookmarks , 在多文件中调试很方便 断点只能在js文件中用,而bookmark可以在所有文件中使用 书签开关 F11  (给光标所在行加书签) 显示书签 Shift + F11 书签号 ...

  4. Java中long(Long)与int(Integer)之间的转换(转)

    一.将long型转化为int型,这里的long型是基础类型: long a = 10; int b = (int)a; 二.将Long型转换为int型,这里的Long型是包装类型: Long a = ...

  5. 临远大神,你为啥要建立一个 TASK表。HumanTaskDTO

    临远大神,你为啥要建立一个 TASK表.HumanTaskDTO HumanTask这张表的作用是什么. 为了实现理想中的任务中心.TaskCenter. 首先,工作流可能会完全不包含任何人工节点,全 ...

  6. Tomcat+Servlet登录页面实例

    概念   Tomcat server是一个免费的开放源码的Web 应用server,属于轻量级应用server,在中小型系统和并发訪问用户不是非常多的场合下被普遍使用,是开发和调试JSP 程序的首选. ...

  7. 负载均衡之基于DNS负载

    基于DNS的负载平衡 OK,在了解了负载平衡系统的大致组成及使用方式之后.我们就来看看各种负载解决方式. 当前业界中所最常使用的负载平衡解决方式主要分为三种:基于DNS的负载平衡,L3/4负载平衡,也 ...

  8. Ajax系列之四:问题总结

    1.最经典的就是ie下的缓存问题了.  假设使用的是get.那么在ie下出现缓存问题.导致代码仅仅运行一次. 解决的方法就是加时间戳或者随机数,使url变为唯一,这样就不会出现ie  下的缓存问题了, ...

  9. 分享一个纯css制作的动画化,在网页(手机)载入等的时候能够引用!

    CSS代码例如以下: /* Custom Stylesheet */ body, html { margin: 0; -webkit-font-smoothing: antialiased; back ...

  10. Make 编译脚本上手

    考察下面的示例代码: main.c #include <stdio.h> int main(){ printf("hello world!"); return 0; } ...