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

Рик Андерсон, Понант и Джо Audette

В этом руководстве показано, как создать приложение ASP.NET Core с подтверждением электронной почты и сбросом пароля. Это руководство не является начальной статьей. Предполагается, что вы знакомы со следующими темами.

Сведения Blazor, которые дополняют или заменяют рекомендации, приведенные в этой статье, см. в следующих ресурсах:

Предварительные требования

Создание и тестирование веб-приложения с проверкой подлинности

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

dotnet new webapp -au Individual -o WebPWrecover
cd WebPWrecover
dotnet run

Регистрация пользователя с имитацией подтверждения электронной почты

Запустите приложение, выберите ссылку "Регистрация " и зарегистрируйте пользователя.

После завершения регистрации вы будете перенаправлены на /Identity/Account/RegisterConfirmation страницу, содержащую ссылку для имитации подтверждения электронной почты.

  1. Выберите ссылку Click here to confirm your account.

  2. Выберите ссылку для входа и войдите с теми же учетными данными.

  3. Выберите ссылку Hello YourEmail@provider.com! , которая перенаправляется на страницу /Identity/Account/Manage/PersonalData .

  4. Перейдите на вкладку "Персональные данные " и нажмите кнопку "Удалить".

Ссылка Click here to confirm your account отображается, так как интерфейс IEmailSender еще не реализован и зарегистрирован в контейнере внедрения зависимостей. Дополнительные сведения см. в источнике RegisterConfirmation.

Примечание.

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

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

В этом руководстве Twilio SendGrid используется для отправки электронной почты. Для отправки электронной почты требуется учетная запись SendGrid и ключ. Мы рекомендуем использовать SendGrid или другую службу электронной почты для отправки электронной почты, а не SMTP. SMTP трудно защитить и настроить правильно.

Для учетной записи SendGrid может потребоваться добавить отправителя.

Создайте класс для получения безопасного ключа электронной почты. В этом примере создайте файл Services/AuthMessageSenderOptions.cs :

namespace WebPWrecover.Services;

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

Настройка секретов пользователя SendGrid

Задайте значение SendGridKey с помощью инструмента secret-manager. Например:

dotnet user-secrets set SendGridKey <key>

Successfully saved SendGridKey to the secret store.

В Windows диспетчер секретов сохраняет пары ключей и значений в файле secrets.json в каталоге %APPDATA%/Microsoft/UserSecrets/<WebAppName-userSecretsId>.

Содержимое файла secrets.json не шифруется. В следующей разметке показан файл secrets.json. Значение SendGridKey удаляется из примера.

{
  "SendGridKey": "<key removed>"
}

Дополнительные сведения см. в шаблоне Options и Configuration в ASP.NET Core.

Установка SendGrid

В этом руководстве показано, как добавить Уведомления по электронной почте через SendGrid, но другие поставщики электронной почты можно использовать.

SendGrid Установите пакет NuGet:

В консоли диспетчер пакетов введите следующую команду:

Install-Package SendGrid

Чтобы зарегистрировать бесплатную учетную запись в SendGrid, начните отправлять электронные письма с помощью бесплатной пробной версии SendGrid Email API.

Реализовать IEmailSender

Чтобы реализовать IEmailSender интерфейс, создайте файл Services/EmailSender.cs с кодом, аналогичным следующему примеру:

using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.Extensions.Options;
using SendGrid;
using SendGrid.Helpers.Mail;

namespace WebPWrecover.Services;

public class EmailSender : IEmailSender
{
    private readonly ILogger _logger;

    public EmailSender(IOptions<AuthMessageSenderOptions> optionsAccessor,
                       ILogger<EmailSender> logger)
    {
        Options = optionsAccessor.Value;
        _logger = logger;
    }

    public AuthMessageSenderOptions Options { get; } //Set with Secret Manager.

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

    public async Task Execute(string apiKey, string subject, string message, string toEmail)
    {
        var client = new SendGridClient(apiKey);
        var msg = new SendGridMessage()
        {
            From = new EmailAddress("Joe@contoso.com", "Password Recovery"),
            Subject = subject,
            PlainTextContent = message,
            HtmlContent = message
        };
        msg.AddTo(new EmailAddress(toEmail));

        // Disable click tracking.
        // See https://sendgrid.com/docs/User_Guide/Settings/tracking.html
        msg.SetClickTracking(false, false);
        var response = await client.SendEmailAsync(msg);
        _logger.LogInformation(response.IsSuccessStatusCode 
                               ? $"Email to {toEmail} queued successfully!"
                               : $"Failure Email to {toEmail}");
    }
}

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

Добавьте следующий код в файл Program.cs , который выполняет следующие задачи:

  • Добавляет экземпляр EmailSender в качестве временной службы.
  • Регистрирует экземпляр конфигурации AuthMessageSenderOptions.
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.EntityFrameworkCore;
using WebPWrecover.Data;
using WebPWrecover.Services;

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlite(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();

builder.Services.AddTransient<IEmailSender, EmailSender>();
builder.Services.Configure<AuthMessageSenderOptions>(builder.Configuration);

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.MapRazorPages();

app.Run();

Отключить проверку учетной записи по умолчанию при добавлении Account.RegisterConfirmation с помощью шаблона

Если Account.RegisterConfirmation создан с помощью шаблона, выполните инструкции в этом разделе.

Important

Если Account.RegisterConfirmationшаблон не установлен, пропустите следующие инструкции и перейдите к следующему разделу.

Пользователь перенаправляется на /Identity/Account/RegisterConfirmation страницу, на которой можно выбрать ссылку, чтобы подтвердить учетную запись. Значение по умолчанию Account.RegisterConfirmation используется только при тестировании. Автоматическая проверка учетной записи должна быть отключена в рабочем приложении.

Чтобы требовать подтверждённую учётную запись и предотвратить автоматический вход сразу после регистрации, задайте DisplayConfirmAccountLink = false в сгенерированном файле /Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs:

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#nullable disable

using System;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.WebUtilities;

namespace WebPWrecover.Areas.Identity.Pages.Account
{
    [AllowAnonymous]
    public class RegisterConfirmationModel : PageModel
    {
        private readonly UserManager<IdentityUser> _userManager;
        private readonly IEmailSender _sender;

        public RegisterConfirmationModel(UserManager<IdentityUser> userManager, IEmailSender sender)
        {
            _userManager = userManager;
            _sender = sender;
        }

        /// <summary>
        ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        public string Email { get; set; }

        /// <summary>
        ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        public bool DisplayConfirmAccountLink { get; set; }

        /// <summary>
        ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        public string EmailConfirmationUrl { get; set; }

        public async Task<IActionResult> OnGetAsync(string email, string returnUrl = null)
        {
            if (email == null)
            {
                return RedirectToPage("/Index");
            }
            returnUrl = returnUrl ?? Url.Content("~/");

            var user = await _userManager.FindByEmailAsync(email);
            if (user == null)
            {
                return NotFound($"Unable to load user with email '{email}'.");
            }

            Email = email;
            // Once you add a real email sender, you should remove this code that lets you confirm the account
            DisplayConfirmAccountLink = false;
            if (DisplayConfirmAccountLink)
            {
                var userId = await _userManager.GetUserIdAsync(user);
                var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
                EmailConfirmationUrl = Url.Page(
                    "/Account/ConfirmEmail",
                    pageHandler: null,
                    values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
                    protocol: Request.Scheme);
            }

            return Page();
        }
    }
}

Этот шаг необходим только в случае, если для Account.RegisterConfirmation выполняется scaffolding.

Не сгенерированная с помощью шаблона страница RegisterConfirmation автоматически определяет, что IEmailSender реализован и зарегистрирован в контейнере внедрения зависимостей.

Регистрация, подтверждение электронной почты и сброс пароля

Запустите веб-приложение и проверьте поток подтверждения учетной записи и восстановления паролей.

  1. Запустите приложение и зарегистрируйте нового пользователя.

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

  3. Выберите ссылку и подтвердите сообщение электронной почты.

  4. Войдите с помощью электронной почты и пароля.

  5. Выйти.

Тестирование сброса пароля

  1. Если вы вошли в систему, выберите "Выйти".

  2. Выберите ссылку "Войти", а затем щелкните ссылку "Забыли пароль"?

  3. Введите сообщение электронной почты, которое вы использовали для регистрации учетной записи. Приложение отправляет сообщение электронной почты со ссылкой на сброс пароля.

  4. Перейдите к отправленной электронной почте.

  5. Перейдите по ссылке и сбросьте пароль.

После успешного сброса пароля вы можете войти с помощью электронной почты и нового пароля.

Повторно отправить письмо с подтверждением

В этом разделе описывается код, поддерживающий процесс подтверждения электронной почты и связанные задачи.

  • Начните с выбора ссылки подтверждения повторной отправки электронной почты на странице входа .

Изменить адрес электронной почты и тайм-аут активности

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

using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.EntityFrameworkCore;
using WebPWrecover.Data;
using WebPWrecover.Services;

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlite(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();

builder.Services.AddTransient<IEmailSender, EmailSender>();
builder.Services.Configure<AuthMessageSenderOptions>(builder.Configuration);

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

var app = builder.Build();

// Code removed for brevity

Изменить сроки действия всех токенов защиты данных

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

using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.EntityFrameworkCore;
using WebPWrecover.Data;
using WebPWrecover.Services;

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlite(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();

builder.Services.AddTransient<IEmailSender, EmailSender>();
builder.Services.Configure<AuthMessageSenderOptions>(builder.Configuration);

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

var app = builder.Build();

// Code removed for brevity.

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

Изменить срок действия токена электронной почты

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

В следующем коде показано, как изменить срок жизни маркера электронной почты.

Добавьте пользовательский DataProtectorTokenProvider<TUser> класс и DataProtectionTokenProviderOptions класс:

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

Добавьте настраиваемого поставщика в контейнер службы:

using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.EntityFrameworkCore;
using WebPWrecover.Data;
using WebPWrecover.Services;
using WebPWrecover.TokenProviders;

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlite(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(config =>
{
    config.SignIn.RequireConfirmedEmail = true;
    config.Tokens.ProviderMap.Add("CustomEmailConfirmation",
        new TokenProviderDescriptor(
            typeof(CustomEmailConfirmationTokenProvider<IdentityUser>)));
    config.Tokens.EmailConfirmationTokenProvider = "CustomEmailConfirmation";
}).AddEntityFrameworkStores<ApplicationDbContext>();

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

builder.Services.AddRazorPages();

builder.Services.AddTransient<IEmailSender, EmailSender>();
builder.Services.Configure<AuthMessageSenderOptions>(builder.Configuration);

var app = builder.Build();

// Code removed for brevity.

Отладка электронной почты

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

  • Задайте точку останова в методе EmailSender.Execute и проверьте, что вызывается метод SendGridClient.SendEmailAsync.

  • Создайте консольное приложение для отправки электронной почты с помощью аналогичного кода EmailSender.Execute.

  • Просмотрите страницу Действия электронной почты.

  • Проверьте папку нежелательной почты.

  • Попробуйте другой псевдоним электронной почты в другом поставщике электронной почты, например Microsoft, Yahoo, Gmail и т. д.

  • Попробуйте отправить в разные учетные записи электронной почты.

Подсказка

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

Объединение учетных записей для входа в социальных сетях и локальных учетных записей входа

Чтобы завершить этот раздел, необходимо сначала включить внешний поставщик проверки подлинности. Дополнительные сведения см. в разделе Использование внешних поставщиков входа с Identity в ASP.NET Core.

В этой последовательности адрес RickAndMSFT@gmail.com электронной почты сначала создается в качестве локального имени входа. Однако сначала вы можете создать учетную запись в качестве имени входа в социальных сетях, а затем добавить локальное имя входа.

  1. Чтобы объединить локальные и социальные учетные записи, выберите ссылку на адрес электронной почты.

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

  2. На странице "Управление учетной записью" выберите ссылку "Управление ".

    Обратите внимание, что в настоящее время с аутентифицированной учетной записью не связано ни одной внешней учетной записи (социального входа) (0).

    Снимок экрана: ноль внешних (социальных имен входа), связанных с учетной записью, прошедшей проверку подлинности. Выделена ссылка

  3. На странице "Управление внешними именами входа" выберите ссылку на другую службу входа. Следуйте инструкциям службы и примите запросы приложения.

    На следующем изображении Facebook добавляется как внешний поставщик проверки подлинности:

    Снимок экрана: Facebook, добавленный как внешний (социальный) вход для учетной записи, прошедшей проверку подлинности.

Проверка подлинности для адреса электронной почты пользователя теперь объединяет локальные и внешние (социальные) учетные записи. Пользователь может войти с помощью любой учетной записи.

Подсказка

Рекомендуется рекомендовать пользователям добавить локальную учетную запись в приложение. Этот подход может помочь обеспечить постоянный доступ в случае, если служба проверки подлинности для входа в социальные сети отключена, или они теряют доступ к своей социальной учетной записи.

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

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

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

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

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

Предварительные требования

Пакет SDK для .NET Core 3.0 или более поздней версии

Создание и тестирование веб-приложения с проверкой подлинности

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

dotnet new webapp -au Individual -uld -o WebPWrecover
cd WebPWrecover
dotnet run

Запустите приложение, выберите ссылку "Регистрация " и зарегистрируйте пользователя. После регистрации вы будете перенаправлены на /Identity/Account/RegisterConfirmation страницу, содержащую ссылку для имитации подтверждения электронной почты:

  • Выберите ссылку Click here to confirm your account.
  • Выберите ссылку для входа и войдите с теми же учетными данными.
  • Выберите ссылку Hello YourEmail@provider.com! , которая перенаправляет вас на страницу /Identity/Account/Manage/PersonalData .
  • Перейдите на вкладку "Персональные данные " слева и нажмите кнопку "Удалить".

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

В этом руководстве SendGrid используется для отправки электронной почты. Вы можете использовать другие поставщики электронной почты. Мы рекомендуем использовать SendGrid или другую службу электронной почты для отправки электронной почты. SMTP сложно настроить так, чтобы почта не помечена как спам.

Для учетной записи SendGrid может потребоваться добавить отправителя.

Создайте класс для получения безопасного ключа электронной почты. Для этого примера создайте Services/AuthMessageSenderOptions.cs:

namespace WebPWrecover.Services;

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

Настройка секретов пользователя SendGrid

Задайте SendGridKey с помощью средства secret-manager. Например:

dotnet user-secrets set SendGridKey <SG.key>

Successfully saved SendGridKey = SG.keyVal to the secret store.

В Windows Secret Manager хранит пары «ключ-значение» в файле secrets.json в каталоге %APPDATA%/Microsoft/UserSecrets/<WebAppName-userSecretsId>.

Содержимое secrets.json файла не шифруется. В следующей разметке показан файл secrets.json. Значение SendGridKey было удалено.

{
  "SendGridKey": "<key removed>"
}

Дополнительные сведения см. в шаблоне параметров и конфигурации.

Установка SendGrid

В этом руководстве показано, как добавить Уведомления по электронной почте через SendGrid, но вы можете отправлять электронную почту с помощью SMTP и других механизмов.

SendGrid Установите пакет NuGet:

В консоли диспетчер пакетов введите следующую команду:

Install-Package SendGrid

См. статью «Начало работы с SendGrid бесплатно», чтобы зарегистрировать бесплатную учётную запись SendGrid.

Реализовать IEmailSender

Чтобы реализовать IEmailSender, создайте Services/EmailSender.cs код, аналогичный следующему:

using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.Extensions.Options;
using SendGrid;
using SendGrid.Helpers.Mail;

namespace WebPWrecover.Services;

public class EmailSender : IEmailSender
{
    private readonly ILogger _logger;

    public EmailSender(IOptions<AuthMessageSenderOptions> optionsAccessor,
                       ILogger<EmailSender> logger)
    {
        Options = optionsAccessor.Value;
        _logger = logger;
    }

    public AuthMessageSenderOptions Options { get; } //Set with Secret Manager.

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

    public async Task Execute(string apiKey, string subject, string message, string toEmail)
    {
        var client = new SendGridClient(apiKey);
        var msg = new SendGridMessage()
        {
            From = new EmailAddress("Joe@contoso.com", "Password Recovery"),
            Subject = subject,
            PlainTextContent = message,
            HtmlContent = message
        };
        msg.AddTo(new EmailAddress(toEmail));

        // Disable click tracking.
        // See https://sendgrid.com/docs/User_Guide/Settings/tracking.html
        msg.SetClickTracking(false, false);
        var response = await client.SendEmailAsync(msg);
        _logger.LogInformation(response.IsSuccessStatusCode 
                               ? $"Email to {toEmail} queued successfully!"
                               : $"Failure Email to {toEmail}");
    }
}

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

Добавьте следующий код в ConfigureServices метод в Startup.cs файле:

  • Добавьте EmailSender в качестве временной службы.
  • Зарегистрируйте экземпляр конфигурации AuthMessageSenderOptions.
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.EntityFrameworkCore;
using WebPWrecover.Data;
using WebPWrecover.Services;

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlite(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();

builder.Services.AddTransient<IEmailSender, EmailSender>();
builder.Services.Configure<AuthMessageSenderOptions>(builder.Configuration);

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.MapRazorPages();

app.Run();

Шаблон RegisterConfirmation

Следуйте инструкциям по шаблону Identity и шаблону Account\RegisterConfirmation.

Отключить проверку учетной записи по умолчанию при добавлении Account.RegisterConfirmation с помощью шаблона

Если Account.RegisterConfirmation создан с помощью шаблона, выполните инструкции в этом разделе.

Important

Если Account.RegisterConfirmationшаблон не установлен, пропустите следующие инструкции и перейдите к следующему разделу.

Пользователь перенаправляется на /Identity/Account/RegisterConfirmation страницу, на которой можно выбрать ссылку, чтобы подтвердить учетную запись. Значение по умолчанию Account.RegisterConfirmation используется только при тестировании. Автоматическая проверка учетной записи должна быть отключена в рабочем приложении.

Чтобы требовать подтверждённую учётную запись и предотвратить автоматический вход сразу после регистрации, задайте DisplayConfirmAccountLink = false в сгенерированном файле /Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs:

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#nullable disable

using System;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.WebUtilities;

namespace WebPWrecover.Areas.Identity.Pages.Account
{
    [AllowAnonymous]
    public class RegisterConfirmationModel : PageModel
    {
        private readonly UserManager<IdentityUser> _userManager;
        private readonly IEmailSender _sender;

        public RegisterConfirmationModel(UserManager<IdentityUser> userManager, IEmailSender sender)
        {
            _userManager = userManager;
            _sender = sender;
        }

        /// <summary>
        ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        public string Email { get; set; }

        /// <summary>
        ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        public bool DisplayConfirmAccountLink { get; set; }

        /// <summary>
        ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        public string EmailConfirmationUrl { get; set; }

        public async Task<IActionResult> OnGetAsync(string email, string returnUrl = null)
        {
            if (email == null)
            {
                return RedirectToPage("/Index");
            }
            returnUrl = returnUrl ?? Url.Content("~/");

            var user = await _userManager.FindByEmailAsync(email);
            if (user == null)
            {
                return NotFound($"Unable to load user with email '{email}'.");
            }

            Email = email;
            // Once you add a real email sender, you should remove this code that lets you confirm the account
            DisplayConfirmAccountLink = false;
            if (DisplayConfirmAccountLink)
            {
                var userId = await _userManager.GetUserIdAsync(user);
                var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
                EmailConfirmationUrl = Url.Page(
                    "/Account/ConfirmEmail",
                    pageHandler: null,
                    values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
                    protocol: Request.Scheme);
            }

            return Page();
        }
    }
}

Этот шаг необходим только в случае, если для Account.RegisterConfirmation выполняется scaffolding.

Не сгенерированная с помощью шаблона страница RegisterConfirmation автоматически определяет, что IEmailSender реализован и зарегистрирован в контейнере внедрения зависимостей.

Регистрация, подтверждение электронной почты и сброс пароля

Запустите веб-приложение и проверьте поток подтверждения учетной записи и восстановления паролей.

  • Запустите приложение и зарегистрируйте нового пользователя
  • Проверьте электронную почту по ссылке подтверждения учетной записи. Если вы не получите сообщение электронной почты, просмотрите отладочную почту .
  • Щелкните ссылку, чтобы подтвердить сообщение электронной почты.
  • Войдите с помощью электронной почты и пароля.
  • Выйти.

Тестирование сброса пароля

  • Если вы вошли в систему, выберите "Выйти".
  • Выберите ссылку "Войти" и щелкните ссылку "Забыл пароль"?
  • Введите сообщение электронной почты, которое вы использовали для регистрации учетной записи.
  • Отправляется сообщение электронной почты со ссылкой на сброс пароля. Проверьте электронную почту и щелкните ссылку, чтобы сбросить пароль. После успешного сброса пароля вы можете войти с помощью электронной почты и нового пароля.

Повторно отправить письмо с подтверждением

В .NET 5 или более поздней версии выберите ссылку подтверждения повторной отправки электронной почты на странице входа .

Изменить адрес электронной почты и тайм-аут активности

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

services.ConfigureApplicationCookie(o => {
    o.ExpireTimeSpan = TimeSpan.FromDays(5);
    o.SlidingExpiration = true;
});

Изменить сроки действия всех токенов защиты данных

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

public void ConfigureServices(IServiceCollection services)
{

    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDefaultIdentity<IdentityUser>(
                  options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();

    services.Configure<DataProtectionTokenProviderOptions>(o =>
       o.TokenLifespan = TimeSpan.FromHours(3));

    services.AddTransient<IEmailSender, EmailSender>();
    services.Configure<AuthMessageSenderOptions>(Configuration);

    services.AddRazorPages();
}

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

Изменить срок действия токена электронной почты

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

Добавьте пользовательские элементы DataProtectorTokenProvider<TUser> и DataProtectionTokenProviderOptions:

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 void ConfigureServices(IServiceCollection services)
{

    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDefaultIdentity<IdentityUser>(config =>
    {
        config.SignIn.RequireConfirmedEmail = true;
        config.Tokens.ProviderMap.Add("CustomEmailConfirmation",
            new TokenProviderDescriptor(
                typeof(CustomEmailConfirmationTokenProvider<IdentityUser>)));
        config.Tokens.EmailConfirmationTokenProvider = "CustomEmailConfirmation";
      }).AddEntityFrameworkStores<ApplicationDbContext>();

    services.AddTransient<CustomEmailConfirmationTokenProvider<IdentityUser>>();

    services.AddTransient<IEmailSender, EmailSender>();
    services.Configure<AuthMessageSenderOptions>(Configuration);

    services.AddRazorPages();
}

Отладка электронной почты

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

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

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

Объединение учетных записей для входа в социальных сетях и локальных учетных записей входа

Чтобы завершить этот раздел, необходимо сначала включить внешний поставщик проверки подлинности. См. аутентификацию через Facebook, Google и внешнего поставщика.

Вы можете объединить локальные и социальные учетные записи, щелкнув ссылку электронной почты. В следующей последовательности "RickAndMSFT@gmail.com" сначала создается в качестве локального входа. Однако сначала можно создать учетную запись в качестве имени входа в социальных сетях, а затем добавить локальное имя входа.

Веб-приложение: RickAndMSFT@gmail.com проверка подлинности пользователя

Щелкните ссылку "Управление ". Обратите внимание, что с этой учетной записью связано 0 внешних учетных данных (входов через социальные сети).

Управление представлением

Щелкните ссылку на другую службу входа и примите запросы приложения. На следующем изображении Facebook является внешним поставщиком проверки подлинности:

Управление внешними именами входа в списке Facebook

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

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

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

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