这是 ASP.NET Core Identity 系列的第四篇文章,上一篇文章讲解了如何在 ASP.NET Core Identity 中实现用户登录与登出。
这篇文章讲一讲如何在 ASP.NET Core Identity 中通过邮件服务实现用户账号的密码重置。
点击上方或后方蓝字,阅读 ASP.NET Core Identity 系列合集。
本篇文章的示例项目:https://github.com/zilor-net/IdentitySample/tree/main/Sample04
密码重置
用户管理中最常见的功能就是密码重置。
密码重置过程,不应该涉及系统管理员,因为用户本身应该能够独立完成整个过程。
通常,登录页面上都会为用户提供忘记密码的链接,以用来重置密码,这就是我们接下来要实现的功能。
简单地解释一下密码重置过程:
用户单击忘记密码链接,然后跳转到带有电子邮件字段的页面。
用户填写该字段后,应用程序会向该电子邮件发送密码重置的连接。
用户通过单击电子邮件的密码重置链接,此时会使用密码重置令牌,重定向到密码重置页面。
用户填充表单中的所有字段后,应用程序将重置密码,用户再被重定向到登录页面或主页。
邮件服务
示例项目中已经集成了邮件服务 「EmailService」 ,以用来帮助我们发送邮件,
具体的邮件发送的实现不是这个系列的主题,就不做过多的阐述。大家可以自己查看示例中「EmailService」项目中关于邮件发送的代码。
邮件服务通过扩展方法注册到了依赖注入框架中,其具体配置在 「appsettings.json」 中。
忘记密码
首先,我们需要创建 「忘记密码」 的视图。
在 「Models」 文件夹中,创建一个 「ForgotPasswordModel」 类:
public class ForgotPasswordModel
{
[Display(Name = "电子邮箱")]
[Required(ErrorMessage = "电子邮箱不能为空")]
[EmailAddress(ErrorMessage = "电子邮箱格式不正确")]
public string Email { get; set; }
}
它会用在「忘记密码」视图中,这里我们只需要获取用户的电子邮件,所以这里只有一个 「Email」 属性。
接下来,在 「Account」 控制器中,创建两个操作方法:
[HttpGet]
public IActionResult ForgotPassword()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ForgotPassword(ForgotPasswordModel forgotPasswordModel)
{
return View(forgotPasswordModel);
}
public IActionResult ForgotPasswordConfirmation()
{
return View();
}
这个套路我们已经很熟悉了,第一个 「ForgotPassword」 只是为了创建视图;第二个 「ForgotPassword」 是为了实现逻辑;「ForgotPasswordConfirmation」 则是返回确认视图。
接下来,再依次创建相关的视图:
<h1>ForgotPasswordConfirmation</h1>
<p>
重置密码的链接已经发送到您的电子邮箱!
</p>
然后在 「Login」 视图中,添加忘记密码的链接:
<div class="form-group">
<a asp-action="ForgotPassword">忘记密码</a>
</div>
现在,让我们来实现忘记密码的逻辑:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ForgotPassword([FromServices]IEmailSender emailSender, ForgotPasswordModel forgotPasswordModel)
{
if (!ModelState.IsValid)
return View(forgotPasswordModel);
var user = await _userManager.FindByEmailAsync(forgotPasswordModel.Email);
if (user == null)
return RedirectToAction(nameof(ForgotPasswordConfirmation));
var token = await _userManager.GeneratePasswordResetTokenAsync(user);
var callback = Url.Action(nameof(ResetPassword), "Account", new { token, email = user.Email }, Request.Scheme);
var message = new Message(new string[] { user.Email }, "重置密码", callback, null);
await emailSender.SendEmailAsync(message);
return RedirectToAction(nameof(ForgotPasswordConfirmation));
}
如果模型有效,就通过用户的电子邮件,从数据库中获取用户。
如果不存在,只需将该用户,重定向到邮件已发送的确认页面,而不是创建用户不存在的消息。
这么做主要是出于安全考虑,以防止有人利用这个功能,验证用户名的有效性。
如果用户存在,就通过 「GeneratePasswordResetTokenAsync」 方法,生成一个令牌,并创建一个回调链接,到我们将用于重置密码逻辑的操作。
最后,我们向用户提供的电子邮件,发送邮件消息,并将用户重定向到确认页面。
现在,程序还无法创建令牌,因为我们还没有注册令牌服务,这需要在注册 「Identity」 方法时进行注册:
builder.Services.AddIdentity<User, IdentityRole>()
.AddEntityFrameworkStores<ApplicationContext>()
.AddDefaultTokenProviders();
如果我们希望密码重置令牌只在有限的时间内有效,例如: 2小时,那我们还需要配置令牌生存期:
builder.Services.Configure<DataProtectionTokenProviderOptions>(opt =>
opt.TokenLifespan = TimeSpan.FromHours(2));
重置密码
接着,我们来实现 「ResetPassword」 重置密码的操作方法,创建一个 「ResetPasswordModel」 类:
public class ResetPasswordModel
{
[Display(Name = "密码")]
[Required(ErrorMessage = "密码不能为空")]
[DataType(DataType.Password)]
public string Password { get; set; }
[Display(Name = "确认密码")]
[DataType(DataType.Password)]
[Compare("Password", ErrorMessage = "密码与确认密码不匹配。")]
public string ConfirmPassword { get; set; }
public string Email { get; set; }
public string Token { get; set; }
}
然后,在 「Account」 控制器中,创建 「ResetPassword」 操作方法:
[HttpGet]
public IActionResult ResetPassword(string token, string email)
{
var model = new ResetPasswordModel { Token = token, Email = email };
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ResetPassword(ResetPasswordModel resetPasswordModel)
{
return View();
}
[HttpGet]
public IActionResult ResetPasswordConfirmation()
{
return View();
}
这里与 「ForgotPassword」 操作类似。
「HttpGet」ResetPassword
操作会接受来自电子邮件中,密码重置连接的请求,提取令牌和电子邮件,并创建一个视图。
「HttpPost」ResetPassword
操作是处理重置密码的逻辑。
ResetPasswordConfirmation
只是一个密码重置的确认视图。
依次创建这些视图:
需要注意的是,我们需要把 「Email」 和 「Token」 两个字段隐藏起来,因为这两个值由应用提供,不需要用户设置:
<input type="hidden" asp-for="Email" class="form-control" />
<input type="hidden" asp-for="Token" class="form-control" />
「ResetPasswordConfirmation」 视图:
<h1>ResetPasswordConfirmation</h1>
<p>
您的密码已经重置. 请点击这里 <a asp-action="Login"> 登录 </a>!
</p>
最后,再来修改 「POST」ResetPassword
操作方法:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ResetPassword(ResetPasswordModel resetPasswordModel)
{
if (!ModelState.IsValid)
return View(resetPasswordModel);
var user = await _userManager.FindByEmailAsync(resetPasswordModel.Email);
if (user == null)
RedirectToAction(nameof(ResetPasswordConfirmation));
var resetPassResult = await _userManager.ResetPasswordAsync(user, resetPasswordModel.Token, resetPasswordModel.Password);
if(!resetPassResult.Succeeded)
{
foreach (var error in resetPassResult.Errors)
{
ModelState.TryAddModelError(error.Code, error.Description);
}
return View();
}
return RedirectToAction(nameof(ResetPasswordConfirmation));
}
首先,检查模型的有效性,以及用户是否存在于数据库中。
之后,使用 「ResetPasswordAsync」 方法,执行密码重置操作。
如果操作失败,就向模型状态添加错误并返回视图。否则,我们将用户重定向到确认页面。
需要注意的是,如果想要测试最终效果,邮件服务的配置以及用户的邮件地址都必须是真实有效的。
小结
现在,我们已经实现了用户通过电子邮件,重置密码的功能,下篇文章将会讲解如何在用户注册时,必须确认电子邮件是否有效的功能。
更多精彩内容,请关注我▼▼
如果喜欢我的文章,那么
在看和转发是对我最大的支持!
(戳下面蓝字阅读)
推荐关注微信公众号:码侠江湖
觉得不错,点个在看再走哟