Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Примечание.
Это не последняя версия этой статьи. В текущей версии см. версию .NET 10 этой статьи.
В этой статье объясняется, как настроить ASP.NET Core Blazor Web App с подтверждением электронной почты и восстановлением паролей.
Примечание.
Эта статья применима только к Blazor Web Apps. Сведения о том, как реализовать подтверждение адреса электронной почты и восстановление пароля для автономных приложений Blazor WebAssembly с ASP.NET Core Identity, см. в статье «Подтверждение учетной записи и восстановление пароля в ASP.NET Core Blazor WebAssembly с ASP.NET Core Identity».
Пространство имен
Пространство имен приложения, используемое примером в этой статье 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. Секрет можно хранить в хранилище ключей для использования в любой среде, включая среду при локальной
Developmentработе. Некоторые разработчики предпочитают использовать хранилища ключей для промежуточных и рабочих развертываний и использовать средство диспетчера секретов для локальной разработки.
Настоятельно рекомендуется избегать хранения секретов в файлах кода проекта или конфигурации. Используйте безопасные потоки проверки подлинности, например оба подхода в этом разделе.
Инструмент управления секретами
Если проект уже инициализирован для Secret Manager, он уже будет содержать идентификатор секретов пользователя (<UserSecretsId>) в файле проекта (.csproj). В Visual Studio можно определить, присутствует ли идентификатор секретов пользователя, просматривая панель свойств при выборе проекта в обозревателе решений. Если приложение не было инициализировано, выполните следующую команду в командной оболочке, открытой в каталоге проекта. В Visual Studio можно использовать командную строку Разработчика PowerShell.
dotnet user-secrets init
Задайте ключ API с помощью средства диспетчера секретов. В следующем примере имя ключа — EmailAuthKey, чтобы соответствовать AuthMessageSenderOptions.EmailAuthKey, а сам ключ обозначается заполнителем {KEY}. Выполните следующую команду с помощью ключа API:
dotnet user-secrets set "EmailAuthKey" "{KEY}"
При использовании Visual Studio можно подтвердить, что секрет задан, щелкнув правой кнопкой мыши проект сервера в Обозреватель решений и выбрав "Управление секретами пользователей".
Дополнительные сведения см. в статье "Безопасное хранилище секретов приложений в разработке".
Предупреждение
Не сохраняйте секреты приложений, строка подключения, учетные данные, пароли, персональные идентификационные номера (ПИН-коды), частный код C#/.NET или закрытые ключи и токены в клиентском коде, который всегда небезопасн. В тестовой, промежуточной и рабочей средах серверный код Blazor и веб-API должны использовать безопасные механизмы аутентификации, которые позволяют избежать хранения учетных данных в коде проекта или файлах конфигурации. Вне локального тестирования разработки рекомендуется избегать использования переменных среды для хранения конфиденциальных данных, так как переменные среды не являются наиболее безопасным подходом. Для локального тестирования разработки средство Secret Manager рекомендуется для защиты конфиденциальных данных. Дополнительные сведения см. в разделе "Безопасное обслуживание конфиденциальных данных и учетных данных".
Azure Key Vault
Azure Key Vault обеспечивает безопасный подход для предоставления секрета клиента приложения приложению.
Чтобы создать хранилище ключей и установить секрет, см. статью Об Azure Key Vault и секретах (документация Azure), которая связывает ресурсы, чтобы начать работу с Azure Key Vault. Например, в этом разделе имя секрета — "EmailAuthKey".
При создании хранилища ключей в портале Entra или Azure:
Настройте хранилище ключей для использования управления доступом на основе ролей Azure (RABC). Если вы не работаете в виртуальной сети Azure, в том числе для локальной разработки и тестирования, убедитесь, что общедоступный доступ на шаге "Сеть " включен (установлен). Включение общедоступного доступа предоставляет только конечную точку хранилища ключей. Учетные записи, прошедшие проверку подлинности, по-прежнему необходимы для доступа.
Создайте управляемую Identity Azure (или добавьте роль в существующий управляемый Identity объект, который вы планируете использовать) с ролью пользователя секретов Key Vault. Назначьте управляемую Identity учетную запись службе приложений Azure, который размещает развертывание: Параметры>Identity>Назначено пользователем>Добавить.
Примечание.
Если вы также планируете локально запускать приложение с авторизованным пользователем для доступа к хранилищу ключей с помощью Azure CLI или проверки подлинности службы Azure Visual Studio, добавьте учетную запись пользователя Azure разработчика в службе управления доступом (IAM) с ролью пользователя секретов Key Vault . Если вы хотите использовать Azure CLI через Visual Studio, выполните
az loginкоманду на панели PowerShell разработчика и следуйте инструкциям по проверке подлинности с помощью клиента.
Чтобы реализовать код в этом разделе, запишите URI хранилища ключей (например, "https://contoso.vault.azure.net/", обязательно с косой чертой в конце) и имя секрета (например, "EmailAuthKey") из Azure при создании хранилища ключей и секрета.
Внимание
Секрет хранилища ключей создается с датой окончания срока действия. Обязательно отслеживайте, когда срок действия секрета хранилища ключей собирается истечь, и создайте новый секрет для приложения до истечения этой даты.
Добавьте следующий класс AzureHelper в серверный проект. Метод GetKeyVaultSecret извлекает секрет из хранилища ключей. Настройте пространство имен (BlazorSample.Helpers), чтобы соответствовать схеме пространства имен проекта.
Helpers/AzureHelper.cs:
using Azure.Core;
using Azure.Security.KeyVault.Secrets;
namespace BlazorSample.Helpers;
public static class AzureHelper
{
public static string GetKeyVaultSecret(string vaultUri,
TokenCredential credential, string secretName)
{
var client = new SecretClient(new Uri(vaultUri), credential);
var secret = client.GetSecretAsync(secretName).Result;
return secret.Value.Value;
}
}
Если службы зарегистрированы в файле Program проекта сервера, получите и свяжите секрет с конфигурацией параметров Options.
TokenCredential? credential;
if (builder.Environment.IsProduction())
{
credential = new ManagedIdentityCredential("{MANAGED IDENTITY CLIENT ID}");
}
else
{
// Local development and testing only
DefaultAzureCredentialOptions options = new()
{
// Specify the tenant ID to use the dev credentials when running the app locally
// in Visual Studio.
VisualStudioTenantId = "{TENANT ID}",
SharedTokenCacheTenantId = "{TENANT ID}"
};
credential = new DefaultAzureCredential(options);
}
var emailAuthKey = AzureHelper.GetKeyVaultSecret("{VAULT URI}", credential,
"EmailAuthKey");
var authMessageSenderOptions =
new AuthMessageSenderOptions() { EmailAuthKey = emailAuthKey };
builder.Configuration.GetSection(authMessageSenderOptions.EmailAuthKey)
.Bind(authMessageSenderOptions);
Примечание.
В несвязанных с Production средами, в приведенном примере используется DefaultAzureCredential для упрощения проверки подлинности при разработке приложений, которые развертываются в Azure, путем объединения учетных данных, используемых в средах размещения Azure, с учетными данными, используемыми при локальной разработке. Дополнительные сведения см. в статье Аутентификация размещенных в Azure приложений .NET в ресурсах Azure с помощью управляемого удостоверения, назначаемого системой.
В предыдущем примере предполагается, что идентификатор управляемого клиента (Identity{MANAGED IDENTITY CLIENT ID}), идентификатор каталога ({TENANT ID}), и URI хранилища ключей ({VAULT URI}, например: https://contoso.vault.azure.net/, требуется завершающая косая черта) предоставляются в виде жестко закодированных значений. Любые или все эти значения можно указать из конфигурации параметров приложения. Например, следующий пример получения URI хранилища из AzureAd узла файла настроек приложения, и vaultUri можно использовать в вызове GetKeyVaultSecret в предыдущем примере.
var vaultUri = builder.Configuration.GetValue<string>("AzureAd:VaultUri")!;
Дополнительные сведения см. в конфигурации ASP.NET Core Blazor.
Реализуйте IEmailSender
Следующий пример основан на API транзакций Mailchimp с помощью Mandrill.net. Сведения о том, как реализовать отправку сообщения электронной почты, см. в документации по другому поставщику.
Добавьте в проект пакет NuGet Mandrill.net.
Добавьте следующий EmailSender класс для реализации IEmailSender<TUser>. В следующем примере ApplicationUser — это IdentityUser. Разметка HTML сообщения может быть дополнительно настроена. Если переданное в MandrillMessage значение message начинается с символа <, 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(ApplicationUser 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(ApplicationUser 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(ApplicationUser 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
Следующий фрагмент кода изменяет время ожидания токенов Data Protection на три часа:
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