[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(Login login)
{
if (ModelState.IsValid)
{
var appUser = await _userManager.FindByEmailAsync(login.Email);
if (appUser != null)
{
await _signInManager.SignOutAsync();
var signInResult = await _signInManager.PasswordSignInAsync(appUser, login.Password,
login.RememberMe, false);
if (signInResult.Succeeded)
{
return Redirect(login.ReturnUrl ?? "/");
}
if (appUser.TwoFactorEnabled)
{
return RedirectToAction("LoginTwoStep", new { Email = appUser.Email, ReturnUrl = login.ReturnUrl });
}
}
ModelState.AddModelError(nameof(login.Email), "Login Failed: Invalid Email or password");
}
return View(login);
}
我们可以看到在登录过程中我们查看用户是否启用双重验证,如果是则会跳转到LoginTwoStep,我们看一下LoginTwoStep是如何工作的,在AccountController中添加LoginTwoStep方法,代码如下:
[AllowAnonymous]
public async Task<IActionResult> LoginTwoStep(string email, string returnUrl)
{
var appUser = await _userManager.FindByEmailAsync(email);
//创建Token
var token = await _userManager.GenerateTwoFactorTokenAsync(appUser ?? new AppUser(), "Email");
//发送邮件
_emailService.Send(appUser?.Email ?? "450190369@qq.com", "授权码", $"<h2>{token}</h2>");
//发送SMS
//_smsService.Send(appUser?.PhoneNumber ?? "13333333333", token);
return View("LoginTwoStep", new TwoFactor { ReturnUrl = returnUrl });
}
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> LoginTwoStep(TwoFactor twoFactor, string returnUrl)
{
if (!ModelState.IsValid)
{
return View("LoginTwoStep", new TwoFactor { TwoFactorCode = twoFactor.TwoFactorCode, ReturnUrl = returnUrl });
}
var result = await _signInManager.TwoFactorSignInAsync("Email", twoFactor.TwoFactorCode, false, false);
if (result.Succeeded)
{
return Redirect(returnUrl ?? "/");
}
else
{
ModelState.AddModelError("", "登录失败");
return View();
}
}
注意:使用UserManager<T>类GenerateTwoFactorTokenAs
public class EmailSetting
{
public string EmailFrom { get; set; } = null!;
public string EmailTo { get; set; } = null!;
public string SmtpHost { get; set; } = null!;
public int SmtpPort { get; set; }
public string SmtpUser { get; set; } = null!;
public string SmtpPass { get; set; } = null!;
}
public interface IEmailService
{
void Send(string to, string subject, string html, string from = null);
}
public class EmailService : IEmailService
{
private readonly EmailSetting _appSettings;
public EmailService(IOptions<EmailSetting> options)
{
_appSettings = options.Value;
}
public void Send(string to, string subject, string html, string from = null)
{
//Create Message
var email = new MimeMessage();
email.From.Add(MailboxAddress.Parse(_appSettings.EmailFrom));
email.To.Add(MailboxAddress.Parse(to));
email.Subject = subject;
email.Body = new TextPart(TextFormat.Html) { Text = html };
//Send Mail
using var smtp = new SmtpClient();
smtp.Connect(_appSettings.SmtpHost, _appSettings.SmtpPort, MailKit.Security.SecureSocketOptions.StartTls);
smtp.Authenticate(_appSettings.SmtpUser, _appSettings.SmtpPass);
smtp.Send(email);
smtp.Disconnect(true);
}
}
最后,TwoFactorSignInAsync 会验证用户的token,TwoFactor类如下:
public class TwoFactor
{
[Required]
[DisplayName("授权码")]
public string TwoFactorCode { get; set; } = null!;
public string? ReturnUrl { get; set; }
}
我们在Views->Account新建一个LoginTwoStep.cshtml视图
@model TwoFactor
@{
ViewData["Title"] = "输入授权码";
}
<div class="container">
<form asp-action="LoginTwoStep" method="post">
<div class="mb-3 row align-items-center">
<div class="col-sm-1">
<label asp-for="TwoFactorCode" class="control-label"></label>
<input type="hidden" name="returnUrl" value="@Model.ReturnUrl" />
</div>
<div class="col-sm-11">
<input asp-for="TwoFactorCode" class="form-control" value="@Model.TwoFactorCode" />
</div>
</div>
<div class="mb-3 row align-items-center">
<div class="col-sm-11 offset-sm-1">
<button class="btn btn-primary" type="submit">登录</button>
</div>
</div>
</form>
</div>
3、测试整个功能
在登录页面我们输入有效的登录凭证,我们看到一个新的页面要求我们输入认证编码。如下图所示
总结
这节我们主要介绍了用户使用邮件进行双因子验证,同样方式我们可以不用修改任何逻辑进行SMS验证
源代码地址:
https://github.com/bingbing-gui/Asp.Net-Core-Skill/tree/master/AspNetCore.Identity/Identity