Подтверждение учетной записи и восстановление паролей в ASP.NET Core Blazor
Примечание.
Это не последняя версия этой статьи. В текущем выпуске см . версию .NET 9 этой статьи.
Внимание
Эта информация относится к предварительному выпуску продукта, который может быть существенно изменен до его коммерческого выпуска. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.
В текущем выпуске см . версию .NET 9 этой статьи.
В этой статье объясняется, как настроить ASP.NET Core Blazor Web App с подтверждением электронной почты и восстановлением паролей.
Примечание.
Эта статья относится только к Blazor Web Apps. Сведения о реализации подтверждения электронной почты и восстановления паролей для автономных Blazor WebAssembly приложений с ASP.NET Core Identityсм. в статье "Подтверждение учетной записи и восстановление паролей" в ASP.NET Core с ASP.NET CoreBlazor WebAssemblyIdentity.
Пространство имен
Пространство имен приложения, используемое примером в этой статье BlazorSample
. Обновите примеры кода, чтобы использовать пространство имен приложения.
Выбор и настройка поставщика электронной почты
В этой статье API транзакций Mailchimp используется через Mandrill.net для отправки электронной почты. Рекомендуется использовать службу электронной почты для отправки электронной почты, а не SMTP. SMTP сложно настроить и защитить должным образом. Независимо от используемой службы электронной почты, получить доступ к их рекомендациям для приложений .NET, создать учетную запись, настроить ключ API для своей службы и установить все необходимые пакеты NuGet.
Создайте класс для хранения ключа API поставщика секретной почты. В примере в этой статье используется класс с именем AuthMessageSenderOptions
свойства EmailAuthKey
для хранения ключа.
AuthMessageSenderOptions.cs
:
namespace BlazorSample;
public class AuthMessageSenderOptions
{
public string? EmailAuthKey { get; set; }
}
AuthMessageSenderOptions
Зарегистрируйте экземпляр конфигурации в Program
файле:
builder.Services.Configure<AuthMessageSenderOptions>(builder.Configuration);
Настройка секрета для ключа безопасности поставщика электронной почты
Получите ключ безопасности поставщика электронной почты от поставщика и используйте его в следующих рекомендациях.
Используйте любой из следующих подходов для предоставления секрета приложению:
- средство диспетчера секретов: средство Secret Manager хранит частные данные на локальном компьютере и используется только во время локальной разработки.
- Azure Key Vault. Вы можете хранить секрет в хранилище ключей для использования в любой среде, включая среду разработки при локальной работе. Некоторые разработчики предпочитают использовать хранилища ключей для промежуточных и рабочих развертываний и использовать средство диспетчера секретов для локальной разработки.
Настоятельно рекомендуется избегать хранения секретов в файлах кода проекта или конфигурации. Используйте безопасные потоки проверки подлинности, например оба подхода в этом разделе.
Инструмент управления секретами
Если проект уже инициализирован для средства Secret Manager, он уже будет иметь идентификатор секретов приложения (<AppSecretsId>
) в файле проекта (.csproj
). В Visual Studio можно определить, присутствует ли идентификатор секретов приложения, просмотрев панель свойств при выборе проекта в Обозреватель решений. Если приложение не было инициализировано, выполните следующую команду в командной оболочке, открытой в каталоге проекта. В Visual Studio можно использовать командную строку Разработчика PowerShell.
dotnet user-secrets init
Задайте ключ API с помощью средства диспетчера секретов. В следующем примере имя ключа соответствует EmailAuthKey
AuthMessageSenderOptions.EmailAuthKey
, а ключ представлен {KEY}
заполнителем. Выполните следующую команду с помощью ключа API:
dotnet user-secrets set "EmailAuthKey" "{KEY}"
При использовании Visual Studio можно подтвердить, что секрет задан, щелкнув правой кнопкой мыши проект сервера в Обозреватель решений и выбрав "Управление секретами пользователей".
Дополнительные сведения см. в статье Надежное хранение секретов приложений при разработке в ASP.NET Core.
Предупреждение
Не сохраняйте секреты приложений, строка подключения, учетные данные, пароли, персональные идентификационные номера (ПИН-коды), частный код C#/.NET или закрытые ключи и токены в клиентском коде, который всегда небезопасн. В средах тестирования и промежуточной и рабочей среды код на стороне Blazor сервера и веб-API должны использовать безопасные потоки проверки подлинности, которые не поддерживают учетные данные в файлах кода проекта или конфигурации. Вне локального тестирования разработки рекомендуется избегать использования переменных среды для хранения конфиденциальных данных, так как переменные среды не являются наиболее безопасным подходом. Для локального тестирования разработки средство Secret Manager рекомендуется для защиты конфиденциальных данных. Дополнительные сведения см. в разделе "Безопасное обслуживание конфиденциальных данных и учетных данных".
Azure Key Vault
Azure Key Vault обеспечивает безопасный подход для предоставления секрета клиента приложения приложению.
Чтобы создать хранилище ключей и установить секрет, см. статью Об Azure Key Vault и секретах (документация Azure), которая связывает ресурсы, чтобы начать работу с Azure Key Vault. Чтобы реализовать код в этом разделе, запишите URI хранилища ключей и имя секрета из Azure при создании хранилища ключей и секрета. При настройке политики доступа для секрета в панели политик доступа:
- Требуется только разрешение на доступ к секрету .
- Выберите приложение в роли основного для секретного ключа.
Убедитесь на портале Azure или Entra, что приложению предоставлен доступ к секрету, созданному для ключа поставщика электронной почты.
Внимание
Секрет хранилища ключей создается с датой окончания срока действия. Обязательно отслеживайте, когда срок действия секрета хранилища ключей собирается истечь, и создайте новый секрет для приложения до истечения этой даты.
Добавьте следующий класс AzureHelper
в серверный проект. Метод GetKeyVaultSecret
извлекает секрет из хранилища ключей. Настройте пространство имен (BlazorSample.Helpers
), чтобы соответствовать схеме пространства имен проекта.
Helpers/AzureHelper.cs
:
using Azure;
using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
namespace BlazorSample.Helpers;
public static class AzureHelper
{
public static string GetKeyVaultSecret(string tenantId, string vaultUri, string secretName)
{
DefaultAzureCredentialOptions options = new()
{
// Specify the tenant ID to use the dev credentials when running the app locally
// in Visual Studio.
VisualStudioTenantId = tenantId,
SharedTokenCacheTenantId = tenantId
};
var client = new SecretClient(new Uri(vaultUri), new DefaultAzureCredential(options));
var secret = client.GetSecretAsync(secretName).Result;
return secret.Value.Value;
}
}
Если службы зарегистрированы в файле Program
проекта сервера, получите и свяжите секрет с конфигурацией параметров Options.
var tenantId = builder.Configuration.GetValue<string>("AzureAd:TenantId")!;
var vaultUri = builder.Configuration.GetValue<string>("AzureAd:VaultUri")!;
var emailAuthKey = AzureHelper.GetKeyVaultSecret(
tenantId, vaultUri, "EmailAuthKey");
var authMessageSenderOptions =
new AuthMessageSenderOptions() { EmailAuthKey = emailAuthKey };
builder.Configuration.GetSection(authMessageSenderOptions.EmailAuthKey)
.Bind(authMessageSenderOptions);
Если вы хотите управлять средой, в которой работает предыдущий код, например, чтобы избежать локального выполнения кода, так как вы решили использовать средство Secret Manager для локальной разработки, можно упаковать предыдущий код в условный оператор, который проверяет среду:
if (!context.HostingEnvironment.IsDevelopment())
{
...
}
В разделе AzureAd
части appsettings.json
серверного проекта подтвердите наличие Entra ID приложения TenantId
и добавьте следующий ключ конфигурации и значение VaultUri
, если они еще не присутствуют:
"VaultUri": "{VAULT URI}"
В предыдущем примере заполнитель {VAULT URI}
является URI хранилища ключей. Включите косую черту в URI.
Пример:
"VaultUri": "https://contoso.vault.azure.net/"
Конфигурация используется для упрощения предоставления выделенных хранилищ ключей и имен секретов на основе файлов конфигурации среды приложения. Например, можно указать различные значения конфигурации для appsettings.Development.json
в разработке, appsettings.Staging.json
при промежуточном выполнении и appsettings.Production.json
для рабочего развертывания. Дополнительные сведения см. в конфигурации ASP.NET Core Blazor.
Реализуйте IEmailSender
Следующий пример основан на API транзакций Mailchimp с помощью Mandrill.net. Сведения о том, как реализовать отправку сообщения электронной почты, см. в документации по другому поставщику.
Добавьте в проект пакет NuGet Mandrill.net.
Добавьте следующий EmailSender
класс для реализации IEmailSender<TUser>. В следующем примере ApplicationUser
используется .IdentityUser Разметка HTML сообщения может быть дополнительно настроена. Если переданный message
MandrillMessage
символ начинается с символа <
, API Mandrill.net предполагает, что текст сообщения состоит в HTML.
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(AppUser user, string email,
string confirmationLink) => SendEmailAsync(email, "Confirm your email",
"<html lang=\"en\"><head></head><body>Please confirm your account by " +
$"<a href='{confirmationLink}'>clicking here</a>.</body></html>");
public Task SendPasswordResetLinkAsync(AppUser user, string email,
string resetLink) => SendEmailAsync(email, "Reset your password",
"<html lang=\"en\"><head></head><body>Please reset your password by " +
$"<a href='{resetLink}'>clicking here</a>.</body></html>");
public Task SendPasswordResetCodeAsync(AppUser user, string email,
string resetCode) => SendEmailAsync(email, "Reset your password",
"<html lang=\"en\"><head></head><body>Please reset your password " +
$"using the following code:<br>{resetCode}</body></html>");
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 дней. Следующий код задает время ожидания бездействия в пять дней с скользящим сроком действия:
builder.Services.ConfigureApplicationCookie(options => {
options.ExpireTimeSpan = TimeSpan.FromDays(5);
options.SlidingExpiration = true;
});
Изменение всех ASP.NET срок действия маркера защиты данных Core
Следующий код изменяет период ожидания маркеров защиты данных на три часа:
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.
- Попробуйте отправить в разные учетные записи электронной почты.
Дополнительные ресурсы
ASP.NET Core