.net core 未发送Core3/React确认电子邮件
此问题适用于具有外部身份提供程序的core3/react项目,创建如下.net core 未发送Core3/React确认电子邮件,.net-core,asp.net-identity,asp.net-core-3.0,.net Core,Asp.net Identity,Asp.net Core 3.0,此问题适用于具有外部身份提供程序的core3/react项目,创建如下 dotnet new react --auth Individual --use-local-db --output conf 并修改为支持外部身份提供程序。该包已添加 dotnet add package Microsoft.AspNetCore.Authentication.MicrosoftAccount 并且启动被修改 services.AddAuthentication() .AddMicrosoftAccou
dotnet new react --auth Individual --use-local-db --output conf
并修改为支持外部身份提供程序。该包已添加
dotnet add package Microsoft.AspNetCore.Authentication.MicrosoftAccount
并且启动被修改
services.AddAuthentication()
.AddMicrosoftAccount(options =>
{
options.ClientId = Configuration["Authentication:Microsoft:ClientId"];
options.ClientSecret = Configuration["Authentication:Microsoft:ClientSecret"];
options.CallbackPath = "/signin-microsoft";
})
之后,我通过注册为用户来测试我的工作。没有抛出错误,但承诺的确认电子邮件从未到达
按照说明末尾的故障排除建议,我在IEmailSender实现的SendEmailAsync方法的开头设置了一个断点,并重复了这个练习。未命中断点
如果我通过更新数据库手动确认帐户
- 我可以登录
- “忘记密码”链接将我带到密码恢复页面,使用该链接将点击我的断点并成功发送一封带有有效链接的密码重置电子邮件
public class SmtpEmailSender : IEmailSender
{
public SmtpEmailSender(IOptions<SmtpOptions> options)
{
this.smtpOptions = options.Value;
}
private SmtpOptions smtpOptions { get; }
public Task SendEmailAsync(string email, string subject, string htmlMessage)
{
var smtp = new SmtpClient();
if (!smtpOptions.ValidateCertificate)
{
smtp.ServerCertificateValidationCallback = (s, c, h, e) => true;
}
smtp.Connect(smtpOptions.Host, smtpOptions.Port, SecureSocketOptions.Auto);
if (smtpOptions.Authenticate)
{
smtp.Authenticate(smtpOptions.Username, smtpOptions.Password);
}
var message = new MimeMessage()
{
Subject = subject,
Body = new BodyBuilder() { HtmlBody = htmlMessage }.ToMessageBody()
};
message.From.Add(new MailboxAddress(smtpOptions.Sender));
message.To.Add(new MailboxAddress(email));
return smtp.SendAsync(FormatOptions.Default, message).ContinueWith(antecedent =>
{
smtp.Disconnect(true);
smtp.Dispose();
});
}
}
SmptOptions只是从appsettings.json中拖出并注入到ctor中的设置。很明显,这一方面是有效的,否则密码重置电子邮件将不起作用
注册不会有任何问题,因为应用程序停止生成需要阅读并遵循我链接的帐户确认说明的消息
为了确定问题是否是由于我的代码无意中产生的副作用造成的,我创建了一个iemailssender
public class DummyEmailSender : IEmailSender
{
private readonly ILogger logger;
public DummyEmailSender(ILogger<DummyEmailSender> logger)
{
this.logger = logger;
}
public Task SendEmailAsync(string email, string subject, string htmlMessage)
{
logger.LogInformation($"SEND EMAIL\r\nemail={email} \r\nsubject={subject}\r\nhtmlMessage={htmlMessage}\r\n{new StackTrace().ToString().Substring(0,500)}");
return Task.CompletedTask;
}
}
看来它应该会起作用。我们知道什么
- 不会抛出任何未经处理的错误,它会传递到RegisterConfirmation,后者会显示一条关于该电子邮件的消息,但该消息永远不会出现
被调用并成功。我们知道这一点,因为用户是在数据库中创建的。所以它肯定会过去,这意味着CreateUser
不是空的,ModelState
是真的.IsValid
- 尽管上面有代码,但实际上并没有调用iemailssender.sendmailasync
- 如果
为真,则会出现一条日志消息,内容类似于“用户使用Microsoft帐户提供程序创建了一个帐户”result.succeed
- 它重定向到
https://localhost:5001/Identity/Account/RegisterConfirmation?Email=accountname@outlook.com
https://localhost:5001/Identity/Account/RegisterConfirmation?Email=accountname@outlook.com
那太疯狂了。为了达到这个目的,userManager.AddLoginAsync()
必须返回true,在这种情况下,下一行是向记录器写入有关创建用户帐户的内容
这毫无意义。你应该自己发送确认电子邮件,它不会自动发送。 注册用户后:
string token = await userManager.GenerateEmailConfirmationTokenAsync(user);
string urltoken = Base64UrlEncoder.Encode(token);
string link = string.Format(emailOptions.ConfirmationUrl, user.Id, urltoken);
string body = $"<a href='{link}'>confirm</a>";
await emailSender.SendEmailAsync(user.Email, "confirmation", body);
string-token=wait-userManager.GenerateEmailConfirmationTokenAsync(用户);
字符串urltoken=Base64UrlEncoder.Encode(令牌);
string link=string.Format(emailOptions.ConfirmationUrl、user.Id、urltoken);
字符串体=$“”;
等待emailSender.SendEmailAsync(user.Email,“确认”,正文);
我创建了一个全新的项目并完成了这个练习。它工作得很好
有什么区别?失败的版本被添加到一个现有的项目中,在解决CICD问题的过程中,该项目在3.0和3.1之间反复出现了好几次。很明显,它以某种不明显的方式损坏了,这不是问题
我没有删除整个问题的唯一原因是其他人可能会掉进这个洞。我使用的是dotnet core 3.1而不是2.x,我会更新这个问题。如果您想查看新玩具,请获取3.1 SDK并生成一个项目
dotnet new react-au Individual
,然后对其进行自定义,以添加对外部身份提供商的支持。更改这两行代码,“wait _emailStore.SetEmailAsync(…);”使用“wait_userManager.CreateAsync(user);”如果我有机会编译3.1分支,我会试试。
public override async Task<IActionResult> OnPostConfirmationAsync(string returnUrl = null)
{
returnUrl = returnUrl ?? Url.Content("~/");
// Get the information about the user from the external login provider
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
{
ErrorMessage = "Error loading external login information during confirmation.";
return RedirectToPage("./Login", new { ReturnUrl = returnUrl });
}
if (ModelState.IsValid)
{
var user = CreateUser();
await _userStore.SetUserNameAsync(user, Input.Email, CancellationToken.None);
await _emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
var result = await _userManager.CreateAsync(user);
if (result.Succeeded)
{
result = await _userManager.AddLoginAsync(user, info);
if (result.Succeeded)
{
_logger.LogInformation("User created an account using {Name} provider.", info.LoginProvider);
var userId = await _userManager.GetUserIdAsync(user);
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { area = "Identity", userId = userId, code = code },
protocol: Request.Scheme);
await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
$"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
// If account confirmation is required, we need to show the link if we don't have a real email sender
if (_userManager.Options.SignIn.RequireConfirmedAccount)
{
return RedirectToPage("./RegisterConfirmation", new { Email = Input.Email });
}
await _signInManager.SignInAsync(user, isPersistent: false);
return LocalRedirect(returnUrl);
}
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
ProviderDisplayName = info.ProviderDisplayName;
ReturnUrl = returnUrl;
return Page();
}
string token = await userManager.GenerateEmailConfirmationTokenAsync(user);
string urltoken = Base64UrlEncoder.Encode(token);
string link = string.Format(emailOptions.ConfirmationUrl, user.Id, urltoken);
string body = $"<a href='{link}'>confirm</a>";
await emailSender.SendEmailAsync(user.Email, "confirmation", body);