启用 RequireConfiredEmail:

    1. public void ConfigureServices(IServiceCollection services)
    2. {
    3. ...
    4. services.Configure<IdentityOptions>(options =>
    5. {
    6. options.Password.RequiredLength = 6;
    7. options.Password.RequireNonAlphanumeric = false;
    8. options.Password.RequireUppercase = false;
    9. options.SignIn.RequireConfirmedEmail = true;
    10. });
    11. ...
    12. }

    普通登录中验证邮箱:

    1. [HttpPost]
    2. public async Task<IActionResult> Login(LoginViewModel model, string returnUrl)
    3. {
    4. model.ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
    5. if (ModelState.IsValid)
    6. {
    7. var user = await _userManager.FindByEmailAsync(model.Email);
    8. if (user != null && !user.EmailConfirmed &&
    9. (await _userManager.CheckPasswordAsync(user, model.Password)))
    10. {
    11. ModelState.AddModelError(string.Empty, "您的电子邮箱尚未进行验证。");
    12. return View(model);
    13. }
    14. var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, false);
    15. if (result.Succeeded)
    16. {
    17. // 防止 Open Redirect
    18. if (!string.IsNullOrEmpty(returnUrl) && Url.IsLocalUrl(returnUrl))
    19. {
    20. return Redirect(returnUrl);
    21. }
    22. return RedirectToAction("Index", "Home");
    23. }
    24. ModelState.AddModelError(string.Empty, "登录失败,请重试");
    25. }
    26. return View(model);
    27. }

    外部登录中验证邮箱:

    1. public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null, string remoteError = null)
    2. {
    3. returnUrl = returnUrl ?? Url.Content("~/");
    4. var loginViewModel = new LoginViewModel
    5. {
    6. ReturnUrl = returnUrl,
    7. ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList()
    8. };
    9. if (remoteError != null)
    10. {
    11. ModelState.AddModelError(string.Empty, $"外部提供程序错误: {remoteError}");
    12. return View("Login", loginViewModel);
    13. }
    14. // 从外部登录提供者,即微软账户体系中,获取关于用户的登录信息。
    15. var info = await _signInManager.GetExternalLoginInfoAsync();
    16. if (info == null)
    17. {
    18. ModelState.AddModelError(string.Empty, "加载外部登录信息出错。");
    19. return View("Login", loginViewModel);
    20. }
    21. ApplicationUser user = null;
    22. // 获取邮箱地址
    23. var email = info.Principal.FindFirstValue(ClaimTypes.Email);
    24. if (email != null)
    25. {
    26. // 通过邮箱地址去查询用户是否已存在
    27. user = await _userManager.FindByEmailAsync(email);
    28. // 如果电子邮件没有被确认,返回登录视图与验证错误
    29. if (user != null && !user.EmailConfirmed)
    30. {
    31. ModelState.AddModelError(string.Empty, "您的电子邮箱尚未进行验证。");
    32. return View("Login", loginViewModel);
    33. }
    34. }
    35. // 如果用户之前已经登录过了,会在 AspNetUserLogins 表有对应的记录,这个时候无需创建新的记录,直接使用当前记录登录系统即可。
    36. var signInResult = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey,
    37. isPersistent: false, bypassTwoFactor: true);
    38. if (signInResult.Succeeded)
    39. {
    40. return LocalRedirect(returnUrl);
    41. }
    42. // 如果AspNetUserLogins表中没有记录,则代表用户没有一个本地帐户,这个时候我们就需要创建一个记录了。
    43. if (email != null)
    44. {
    45. if (user == null)
    46. {
    47. user = new ApplicationUser
    48. {
    49. UserName = info.Principal.FindFirstValue(ClaimTypes.Email),
    50. Email = info.Principal.FindFirstValue(ClaimTypes.Email)
    51. };
    52. //如果不存在,则创建一个用户,但是这个用户没有密码。
    53. await _userManager.CreateAsync(user);
    54. // 在AspNetUserLogins表中,添加一行用户数据,然后将当前用户登录到系统中
    55. await _userManager.AddLoginAsync(user, info);
    56. await _signInManager.SignInAsync(user, isPersistent: false);
    57. return LocalRedirect(returnUrl);
    58. }
    59. }
    60. // 如果我们获取不到电子邮件地址,我们需要将请求重定向到错误视图中。
    61. ViewBag.ErrorTitle = $"我们无法从提供商:{info.LoginProvider}中解析到您的邮件地址 ";
    62. ViewBag.ErrorMessage = "请通过联系 ltm@ddxc.org 寻求技术支持。";
    63. return View("Error");
    64. }

    效果:
    image.png