Подтверждение учетной записи и восстановление паролей в ASP.NET Core Blazor

В этой статье объясняется, как настроить ASP.NET Core Blazor Web App с подтверждением электронной почты и восстановлением паролей.

Пространство имен

Пространство имен приложения, используемое примером в этой статье BlazorSample. Обновите примеры кода, чтобы использовать пространство имен приложения.

Выбор и настройка поставщика электронной почты

В этой статье API транзакций Mailchimp используется через Mandrill.net для отправки электронной почты. Рекомендуется использовать службу электронной почты для отправки электронной почты, а не SMTP. SMTP сложно настроить и защитить должным образом. Независимо от используемой службы электронной почты, получить доступ к их рекомендациям для приложений .NET, создать учетную запись, настроить ключ API для своей службы и установить все необходимые пакеты NuGet.

Создайте класс для получения ключа API безопасной электронной почты. В примере в этой статье используется класс с именем AuthMessageSenderOptions свойства EmailAuthKey для хранения ключа.

AuthMessageSenderOptions:

namespace BlazorSample;

public class AuthMessageSenderOptions
{
    public string? EmailAuthKey { get; set; }
}

AuthMessageSenderOptions Зарегистрируйте экземпляр конфигурации в Program файле:

builder.Services.Configure<AuthMessageSenderOptions>(builder.Configuration);

Настройка секрета пользователя для ключа безопасности поставщика

Задайте ключ с помощью средства secret-manager. В следующем примере имя ключа — EmailAuthKey и ключ представлен {KEY} заполнителем. В командной оболочке перейдите в корневую папку приложения и выполните следующую команду с помощью ключа API:

dotnet user-secrets set "EmailAuthKey" "{KEY}"

Дополнительные сведения см. в статье Надежное хранение секретов приложений при разработке в ASP.NET Core.

Реализуйте IEmailSender

Реализуйте IEmailSender для поставщика. Следующий пример основан на API транзакций Mailchimp с помощью Mandrill.net. Сведения о том, как реализовать отправку сообщения в методе, см. в документации по другому поставщику Execute .

Components/Account/EmailSender.cs:

using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using Mandrill;
using Mandrill.Model;
using BlazorSample.Data;

namespace BlazorSample.Components.Account;

public class EmailSender(IOptions<AuthMessageSenderOptions> optionsAccessor,
    ILogger<EmailSender> logger) : IEmailSender<ApplicationUser>
{
    private readonly ILogger logger = logger;

    public AuthMessageSenderOptions Options { get; } = optionsAccessor.Value;

    public Task SendConfirmationLinkAsync(ApplicationUser user, string email, 
        string confirmationLink) => SendEmailAsync(email, "Confirm your email", 
        $"Please confirm your account by " +
        "<a href='{confirmationLink}'>clicking here</a>.");

    public Task SendPasswordResetLinkAsync(ApplicationUser user, string email, 
        string resetLink) => SendEmailAsync(email, "Reset your password", 
        $"Please reset your password by <a href='{resetLink}'>clicking here</a>.");

    public Task SendPasswordResetCodeAsync(ApplicationUser user, string email, 
        string resetCode) => SendEmailAsync(email, "Reset your password", 
        $"Please reset your password using the following code: {resetCode}");

    public async Task SendEmailAsync(string toEmail, string subject, string message)
    {
        if (string.IsNullOrEmpty(Options.EmailAuthKey))
        {
            throw new Exception("Null EmailAuthKey");
        }

        await Execute(Options.EmailAuthKey, subject, message, toEmail);
    }

    public async Task Execute(string apiKey, string subject, string message, 
        string toEmail)
    {
        var api = new MandrillApi(apiKey);
        var mandrillMessage = new MandrillMessage("sarah@contoso.com", toEmail, 
            subject, message);
        await api.Messages.SendAsync(mandrillMessage);

        logger.LogInformation("Email to {EmailAddress} sent!", toEmail);
    }
}

Примечание.

Содержимое текста для сообщений может потребовать специального кодирования для поставщика услуг электронной почты. Если ссылки в тексте сообщения не могут быть выполнены, обратитесь к документации поставщика услуг.

Настройка приложения для поддержки электронной почты

Program В файле измените реализацию отправителя электронной почты на EmailSender:

- builder.Services.AddSingleton<IEmailSender<ApplicationUser>, IdentityNoOpEmailSender>();
+ builder.Services.AddSingleton<IEmailSender<ApplicationUser>, EmailSender>();

IdentityNoOpEmailSender Удалите (Components/Account/IdentityNoOpEmailSender.cs) из приложения.

В компоненте RegisterConfirmation (Components/Account/Pages/RegisterConfirmation.razor) удалите условный блок в блоке@code, который проверка, если EmailSender это IdentityNoOpEmailSender:

- else if (EmailSender is IdentityNoOpEmailSender)
- {
-     ...
- }

Кроме того, в RegisterConfirmation компоненте удалите Razor разметку и код для проверка поляemailConfirmationLink, оставив только строку, в результате чего пользователь проверка свою электронную почту ...

- @if (emailConfirmationLink is not null)
- {
-     ...
- }
- else
- {
     <p>Please check your email to confirm your account.</p>
- }

@code {
-    private string? emailConfirmationLink;

     ...
}

Время ожидания электронной почты и действий

Время ожидания бездействия по умолчанию — 14 дней. Следующий код задает время ожидания бездействия в течение 5 дней с скользящим сроком действия:

builder.Services.ConfigureApplicationCookie(options => {
    options.ExpireTimeSpan = TimeSpan.FromDays(5);
    options.SlidingExpiration = true;
});

Изменение срока жизни маркера защиты данных

Следующий код изменяет период ожидания всех маркеров защиты данных на 3 часа:

builder.Services.Configure<DataProtectionTokenProviderOptions>(options =>
    options.TokenLifespan = TimeSpan.FromHours(3));

Встроенные Identity маркеры пользователей (AspNetCore/src/IdentityExtensions.Core/src/TokenOptions.cs) имеют однодневное время ожидания.

Примечание.

По ссылкам в документации на справочные материалы по .NET обычно загружается ветвь репозитория по умолчанию, которая представляет текущую разработку для следующего выпуска .NET. Чтобы выбрать тег для определенного выпуска, используйте раскрывающийся список Switch branches or tags (Переключение ветвей или тегов). Дополнительные сведения см. в статье Выбор тега версии исходного кода ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Изменение срока жизни маркера электронной почты

Срок действия маркера по умолчанию для маркеров Identity пользователя составляет один день.

Примечание.

По ссылкам в документации на справочные материалы по .NET обычно загружается ветвь репозитория по умолчанию, которая представляет текущую разработку для следующего выпуска .NET. Чтобы выбрать тег для определенного выпуска, используйте раскрывающийся список Switch branches or tags (Переключение ветвей или тегов). Дополнительные сведения см. в статье Выбор тега версии исходного кода ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Чтобы изменить срок жизни маркера электронной почты, добавьте настраиваемый DataProtectorTokenProvider<TUser> и DataProtectionTokenProviderOptions:

CustomTokenProvider.cs:

using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;

namespace BlazorSample;

public class CustomEmailConfirmationTokenProvider<TUser>
    : DataProtectorTokenProvider<TUser> where TUser : class
{
    public CustomEmailConfirmationTokenProvider(
        IDataProtectionProvider dataProtectionProvider,
        IOptions<EmailConfirmationTokenProviderOptions> options,
        ILogger<DataProtectorTokenProvider<TUser>> logger)
        : base(dataProtectionProvider, options, logger)
    {
    }
}

public class EmailConfirmationTokenProviderOptions 
    : DataProtectionTokenProviderOptions
{
    public EmailConfirmationTokenProviderOptions()
    {
        Name = "EmailDataProtectorTokenProvider";
        TokenLifespan = TimeSpan.FromHours(4);
    }
}

public class CustomPasswordResetTokenProvider<TUser> 
    : DataProtectorTokenProvider<TUser> where TUser : class
{
    public CustomPasswordResetTokenProvider(
        IDataProtectionProvider dataProtectionProvider,
        IOptions<PasswordResetTokenProviderOptions> options,
        ILogger<DataProtectorTokenProvider<TUser>> logger)
        : base(dataProtectionProvider, options, logger)
    {
    }
}

public class PasswordResetTokenProviderOptions : 
    DataProtectionTokenProviderOptions
{
    public PasswordResetTokenProviderOptions()
    {
        Name = "PasswordResetDataProtectorTokenProvider";
        TokenLifespan = TimeSpan.FromHours(3);
    }
}

Настройте службы для использования пользовательского поставщика маркеров в Program файле:

builder.Services.AddIdentityCore<ApplicationUser>(options =>
    {
        options.SignIn.RequireConfirmedAccount = true;
        options.Tokens.ProviderMap.Add("CustomEmailConfirmation",
            new TokenProviderDescriptor(
                typeof(CustomEmailConfirmationTokenProvider<ApplicationUser>)));
        options.Tokens.EmailConfirmationTokenProvider = 
            "CustomEmailConfirmation";
    })
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddSignInManager()
    .AddDefaultTokenProviders();

builder.Services
    .AddTransient<CustomEmailConfirmationTokenProvider<ApplicationUser>>();

Устранение неполадок

Если вы не можете получить электронную почту, выполните следующие действия:

  • Задайте точку останова для EmailSender.Execute проверки SendEmailAsync .
  • Создайте консольное приложение для отправки электронной почты с помощью кода, EmailSender.Execute аналогичного отладке проблемы.
  • Просмотрите страницы журнала электронной почты учетной записи на веб-сайте поставщика электронной почты.
  • Проверьте папку нежелательной почты для сообщений.
  • Попробуйте другой псевдоним электронной почты в другом поставщике электронной почты, например Microsoft, Yahoo или Gmail.
  • Попробуйте отправить в разные учетные записи электронной почты.

Предупреждение

Не используйте рабочие секреты в тестировании и разработке. При публикации приложения в Azure задайте секреты в качестве параметров приложения на портале веб-приложения Azure. Система конфигурации настроена для чтения ключей из переменных среды.

Включение подтверждения учетной записи после того, как сайт имеет пользователей

Включение подтверждения учетной записи на сайте с пользователями блокирует всех существующих пользователей. Существующие пользователи заблокированы, так как их учетные записи не подтверждены. Чтобы обойти существующую блокировку пользователей, используйте один из следующих подходов:

  • Обновите базу данных, чтобы пометить всех существующих пользователей как подтвержденную.
  • Подтвердите существующих пользователей. Например, пакетная отправка сообщений электронной почты с ссылками подтверждения.

Дополнительные ресурсы