Core3 / React确认电子邮件未发送

问题描述 投票:0回答:2

此问题适用于具有外部身份提供者的core3 / react项目,其创建方式如下。

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";
})

[遵循instructions provided by Microsoft之后,我通过注册为用户来测试我的作品。没有引发任何错误,但承诺的确认电子邮件从未到达。

按照说明末尾的故障排除建议,我在实现IEmailSender的SendEmailAsync方法的开始处设置了一个断点,并重复了该练习。断点未命中。

如果我通过更新数据库手动确认帐户,则>>

  • 我可以登录。
  • “忘记密码”链接将我带到密码恢复页面,并使用该链接击中了我的断点,并成功发送了带有有效链接的密码重置电子邮件。
  • 很明显,我的IEmailSender实现有效并且已正确注册。它与示例代码并不完全相同,因为我拥有自己的Exchange服务器,并且未使用SendGrid,但是它成功发送了一封电子邮件以重置密码,因此我可以无限制地重复执行任意次。

针对某种原因导致问题的可能性很小,这是我的实现方式

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();
        });
    }
}

startup.cs中的注册看起来像这样。

            services.AddTransient<IEmailSender, SmtpEmailSender>();
            services.Configure<SmtpOptions>(Configuration.GetSection("SmtpOptions"));

SmptOptions只是从appsettings.json中拖出的设置,并已注入到ctor中。显然,方面有效,否则密码重置电子邮件将无效。

注册没有任何问题,因为该应用程序停止生成有关需要阅读并遵循我链接的帐户确认说明的消息。

[查看问题是否是由于我的代码的某些无意副作用引起的,我创建了一个已检测到的IEmailSender存根

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;
    }
}

我还更新了服务注册以进行匹配。

这是可能的最简单的检测存根,并且观察到的行为是相同的,在提交“忘记密码”表单时将调用它,在提交“确认注册”表单时将<。

有没有人做过可怕的事情?怎么样?


在失败之前,此URL https://wone.pdconsec.net/Identity/Account/ExternalLogin?returnUrl=%2Fauthentication%2Flogin&handler=Callback看起来像这样

enter image description here

检查页面,我们发现注册按钮将表格发送到/Identity/Account/ExternalLogin?returnUrl=%2Fauthentication%2Flogin&amp;handler=Confirmation

此代码可从dotnet存储库中获得。克隆存储库https://github.com/dotnet/aspnetcore.git之后,我阅读了build instructions并成功构建了dotnet 5预览。然后我运行了clean,然后切换到带标签的分支release/3.1以为core3.1构建调试程序包,但这失败了,因为带标签的分支启用了一个版本太旧的msbuild版本,并且错误消息建议了补救措施似乎不起作用。由于我对PowerShell的掌握不强(构建脚本为PowerShell),因此我只能进行代码检查。相关代码如下所示。

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(); }

看来应该可以使用。我们知道什么?

    不会引发任何未处理的错误,它会传递到RegisterConfirmation,后者会显示有关永不出现的电子邮件的消息。
  • CreateUser被调用并成功。我们知道这是因为用户是在数据库中创建的。因此它肯定已经过去了,这意味着ModelState不为空,.IsValid为真。
  • 尽管上面的代码,但实际上并未调用IEmailSender.SendEmailAsync。
  • 如果result.Succeeded为true,则应该显示一条日志消息,例如“用户使用Microsoft帐户提供程序创建帐户”
  • 它重定向到https://localhost:5001/Identity/Account/[email protected]
  • 我在大多数情况下都看到日志消息。尝试在第一次通过创建用户但无法发送电子邮件后第二次尝试注册时,控制台和事件日志中会出现有关DuplicateUserName的警告。直接在数据库中设置确认,我们可以登录,然后以交互方式删除该帐户,并显示这些活动的日志。
  • 但是没有日志可确认。

    really

进入的事实是它随后重定向到https://localhost:5001/Identity/Account/[email protected]太疯狂了。为了到达那里,userManager.AddLoginAsync()必须返回true,在这种情况下的下一行是写给记录器的关于创建用户帐户的信息。

这没有道理。

此问题适用于具有外部身份提供者的core3 / react项目,其创建方式如下。 dotnet new react --auth单个--use-local-db --output conf,并进行了修改以支持...

.net-core asp.net-identity asp.net-core-3.0
2个回答
0
投票
您应该自己发送确认电子邮件,它不会自动发送。注册用户后:

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);


0
投票
我创建了一个全新的项目并进行了练习。它运行完美。
最新问题
© www.soinside.com 2019 - 2024. All rights reserved.