Udostępnij za pośrednictwem


Potwierdzenie konta i odzyskiwanie hasła w usłudze ASP.NET Core

Przez Rick Anderson, Ponant i Joe Audette

W tym samouczku pokazano, jak utworzyć aplikację ASP.NET Core z potwierdzeniem wiadomości e-mail i resetowaniem hasła. Ten samouczek nie jest tematem początkowym. Należy zapoznać się z:

Aby uzyskać Blazor wskazówki, które dodają lub zastępują wskazówki zawarte w tym artykule, zobacz Potwierdzanie konta i odzyskiwanie hasła w usłudze ASP.NET Core Blazor.

Wymagania wstępne

Tworzenie i testowanie aplikacji internetowej przy użyciu uwierzytelniania

Uruchom następujące polecenia, aby utworzyć aplikację internetową z uwierzytelnianiem.

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

Rejestrowanie użytkownika przy użyciu symulowanego potwierdzenia wiadomości e-mail

Uruchom aplikację, wybierz link Zarejestruj i zarejestruj użytkownika. Po zarejestrowaniu /Identity/Account/RegisterConfirmation nastąpi przekierowanie do strony zawierającej link do symulowania potwierdzenia wiadomości e-mail:

  • Wybierz link Click here to confirm your account.
  • Wybierz link Zaloguj się i zaloguj się przy użyciu tych samych poświadczeń.
  • Hello YourEmail@provider.com! Wybierz link, który przekierowuje do /Identity/Account/Manage/PersonalData strony.
  • Wybierz kartę Dane osobowe po lewej stronie, a następnie wybierz pozycję Usuń.

Link Click here to confirm your account jest wyświetlany, ponieważ element IEmailSender nie został zaimplementowany i zarejestrowany w kontenerze wstrzykiwania zależności. RegisterConfirmation Zobacz źródło.

Uwaga

Linki dokumentacji do źródła referencyjnego platformy .NET zwykle ładują domyślną gałąź repozytorium, która odzwierciedla bieżące programowanie dla następnej wersji platformy .NET. Aby wybrać tag dla określonej wersji, użyj listy rozwijanej Przełącz gałęzie lub tagi. Aby uzyskać więcej informacji, zobacz Jak wybrać tag wersji kodu źródłowego platformy ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Konfigurowanie dostawcy poczty e-mail

W tym samouczku usługa SendGrid służy do wysyłania wiadomości e-mail. Do wysyłania wiadomości e-mail jest wymagane konto i klucz usługi SendGrid. Zalecamy używanie usługi SendGrid lub innej usługi poczty e-mail do wysyłania wiadomości e-mail zamiast SMTP. Protokół SMTP jest trudny do zabezpieczenia i poprawnie skonfigurowany.

Konto usługi SendGrid może wymagać dodania nadawcy.

Utwórz klasę, aby pobrać bezpieczny klucz poczty e-mail. W tym przykładzie utwórz polecenie Services/AuthMessageSenderOptions.cs:

namespace WebPWrecover.Services;

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

Konfigurowanie wpisów tajnych użytkownika usługi SendGrid

Ustaw element SendGridKey za pomocą narzędzia secret-manager. Na przykład:

dotnet user-secrets set SendGridKey <key>

Successfully saved SendGridKey to the secret store.

W systemie Windows menedżer wpisów tajnych przechowuje pary kluczy/wartości w secrets.json pliku w %APPDATA%/Microsoft/UserSecrets/<WebAppName-userSecretsId> katalogu.

Zawartość secrets.json pliku nie jest szyfrowana. Poniższy znacznik pokazuje secrets.json plik. Wartość została usunięta SendGridKey .

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

Aby uzyskać więcej informacji, zobacz Wzorzec i konfiguracja opcji.

Instalowanie usługi SendGrid

W tym samouczku pokazano, jak dodawać powiadomienia e-mail za pośrednictwem usługi SendGrid, ale mogą być używane inne dostawcy poczty e-mail.

SendGrid Zainstaluj pakiet NuGet:

W konsoli Menedżer pakietów wprowadź następujące polecenie:

Install-Package SendGrid

Zobacz Wprowadzenie do usługi SendGrid bezpłatnie , aby zarejestrować się w celu uzyskania bezpłatnego konta usługi SendGrid.

Implementowanie usługi IEmailSender

Aby zaimplementować IEmailSendermetodę , utwórz Services/EmailSender.cs za pomocą kodu podobnego do następującego:

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

Konfigurowanie aplikacji do obsługi poczty e-mail

Dodaj następujący kod do pliku Program.cs:

  • Dodaj EmailSender jako usługę przejściową.
  • AuthMessageSenderOptions Zarejestruj wystąpienie konfiguracji.
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();

Wyłącz domyślną weryfikację konta, gdy element Account.RegisterConfirmation został utworzony szkieletem

Ta sekcja ma zastosowanie tylko w przypadku Account.RegisterConfirmation tworzenia szkieletów. Pomiń tę sekcję, jeśli nie utworzono szkieletu Account.RegisterConfirmation.

Użytkownik jest przekierowywany do Account.RegisterConfirmation lokalizacji, w której może wybrać link, aby potwierdzić konto. Wartość domyślna Account.RegisterConfirmation jest używana tylko do testowania, automatyczna weryfikacja konta powinna być wyłączona w aplikacji produkcyjnej.

Aby wymagać potwierdzonego konta i uniemożliwić natychmiastowe logowanie podczas rejestracji, ustaw DisplayConfirmAccountLink = false w pliku szkieletowym /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();
        }
    }
}

Ten krok jest niezbędny tylko wtedy, gdy Account.RegisterConfirmation jest rusztowany. Niesfałdowany element RegisterConfirmation automatycznie wykrywa, kiedy element IEmailSender został zaimplementowany i zarejestrowany w kontenerze wstrzykiwania zależności.

Rejestrowanie, potwierdzanie wiadomości e-mail i resetowanie hasła

Uruchom aplikację internetową i przetestuj przepływ potwierdzenia konta i odzyskiwania hasła.

  • Uruchamianie aplikacji i rejestrowanie nowego użytkownika
  • Sprawdź adres e-mail, aby uzyskać link potwierdzenia konta. Jeśli nie otrzymasz wiadomości e-mail, zobacz Debugowanie wiadomości e-mail .
  • Kliknij link, aby potwierdzić twoją wiadomość e-mail.
  • Zaloguj się przy użyciu adresu e-mail i hasła.
  • Wyloguj się.

Testowanie resetowania hasła

  • W przypadku zalogowania wybierz pozycję Wyloguj.
  • Wybierz link Zaloguj się i wybierz link Nie pamiętasz hasła?
  • Wprowadź adres e-mail użyty do zarejestrowania konta.
  • Zostanie wysłana wiadomość e-mail z linkiem umożliwiającym zresetowanie hasła. Sprawdź swój adres e-mail i kliknij link, aby zresetować hasło. Po pomyślnym zresetowaniu hasła możesz zalogować się przy użyciu poczty e-mail i nowego hasła.

Wyślij ponownie wiadomość e-mail z potwierdzeniem

Wybierz link Potwierdź ponownie wiadomość e-mail na stronie Logowanie.

Zmienianie limitu czasu wiadomości e-mail i działań

Domyślny limit czasu braku aktywności wynosi 14 dni. Poniższy kod ustawia limit czasu braku aktywności na 5 dni:

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

Zmienianie wszystkich cykli życia tokenu ochrony danych

Poniższy kod zmienia limit czasu wszystkich tokenów ochrony danych na 3 godziny:

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.

Wbudowane Identity tokeny użytkownika (zobacz AspNetCore/src//IdentityExtensions.Core/src/TokenOptions.cs )mają jednorazowy limit czasu.

Zmienianie cyklu życia tokenu poczty e-mail

Domyślna żywotność tokenów Identity użytkownika wynosi jeden dzień. W tej sekcji pokazano, jak zmienić cykl życia tokenu poczty e-mail.

Dodaj niestandardowe DataProtectorTokenProvider<TUser> i 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);
    }
}

Dodaj dostawcę niestandardowego do kontenera usługi:

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.

Debugowanie wiadomości e-mail

Jeśli nie możesz uzyskać wiadomości e-mail, działa:

  • Ustaw punkt przerwania w , EmailSender.Execute aby zweryfikować SendGridClient.SendEmailAsync , jest wywoływany.
  • Utwórz aplikację konsolową do wysyłania wiadomości e-mail przy użyciu kodu podobnego do EmailSender.Execute.
  • Przejrzyj stronę Działanie poczty e-mail.
  • Sprawdź folder spamu.
  • Wypróbuj inny alias poczty e-mail u innego dostawcy poczty e-mail (Microsoft, Yahoo, Gmail itp.)
  • Spróbuj wysłać do różnych kont e-mail.

Najlepszym rozwiązaniem w zakresie zabezpieczeń jest brak używania wpisów tajnych produkcyjnych podczas testowania i programowania. Jeśli opublikujesz aplikację na platformie Azure, ustaw wpisy tajne usługi SendGrid jako ustawienia aplikacji w portalu aplikacji internetowej platformy Azure. System konfiguracji jest skonfigurowany do odczytu kluczy ze zmiennych środowiskowych.

Łączenie kont logowania społecznościowego i lokalnego

Aby ukończyć tę sekcję, musisz najpierw włączyć zewnętrznego dostawcę uwierzytelniania. Zobacz Uwierzytelnianie za pomocą usług Facebook, Google i zewnętrznego dostawcy.

Możesz połączyć konta lokalne i społecznościowe, klikając link e-mail. W poniższej sekwencji element "RickAndMSFT@gmail.com" jest najpierw tworzony jako identyfikator logowania lokalnego, ale najpierw możesz utworzyć konto jako identyfikator logowania społecznościowego, a następnie dodać nazwę logowania lokalnego.

Aplikacja internetowa: RickAndMSFT@gmail.com uwierzytelniony użytkownik

Kliknij link Zarządzaj. Zwróć uwagę na 0 zewnętrznych (identyfikatorów logowania społecznościowego) skojarzonych z tym kontem.

Zarządzanie widokiem

Kliknij link do innej usługi logowania i zaakceptuj żądania aplikacji. Na poniższej ilustracji Facebook jest zewnętrznym dostawcą uwierzytelniania:

Zarządzanie wyświetlaniem zewnętrznych identyfikatorów logowania na facebooku

Te dwa konta zostały połączone. Możesz zalogować się przy użyciu dowolnego konta. Możesz chcieć, aby użytkownicy dodawali konta lokalne w przypadku, gdy usługa uwierzytelniania logowania społecznościowego nie działa lub prawdopodobnie utraciła dostęp do konta społecznościowego.

Włączanie potwierdzenia konta po tym, jak witryna ma użytkowników

Włączanie potwierdzenia konta w witrynie z użytkownikami blokuje wszystkich istniejących użytkowników. Istniejący użytkownicy są zablokowani, ponieważ ich konta nie są potwierdzane. Aby obejść istniejącą blokadę użytkownika, użyj jednej z następujących metod:

  • Zaktualizuj bazę danych, aby oznaczyć wszystkich istniejących użytkowników jako potwierdzonych.
  • Potwierdź istniejących użytkowników. Na przykład wiadomości e-mail wysyłane wsadowo z linkami potwierdzenia.

Wymagania wstępne

Zestaw .NET Core 3.0 SDK lub nowszy

Tworzenie i testowanie aplikacji internetowej przy użyciu uwierzytelniania

Uruchom następujące polecenia, aby utworzyć aplikację internetową z uwierzytelnianiem.

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

Uruchom aplikację, wybierz link Zarejestruj i zarejestruj użytkownika. Po zarejestrowaniu /Identity/Account/RegisterConfirmation nastąpi przekierowanie do strony zawierającej link do symulowania potwierdzenia wiadomości e-mail:

  • Wybierz link Click here to confirm your account.
  • Wybierz link Zaloguj się i zaloguj się przy użyciu tych samych poświadczeń.
  • Hello YourEmail@provider.com! Wybierz link, który przekierowuje Cię do /Identity/Account/Manage/PersonalData strony.
  • Wybierz kartę Dane osobowe po lewej stronie, a następnie wybierz pozycję Usuń.

Konfigurowanie dostawcy poczty e-mail

W tym samouczku usługa SendGrid służy do wysyłania wiadomości e-mail. Możesz użyć innych dostawców poczty e-mail. Zalecamy wysyłanie wiadomości e-mail przy użyciu usługi SendGrid lub innej usługi poczty e-mail. Protokół SMTP jest trudny do skonfigurowania, więc poczta nie jest oznaczona jako spam.

Konto usługi SendGrid może wymagać dodania nadawcy.

Utwórz klasę, aby pobrać bezpieczny klucz poczty e-mail. W tym przykładzie utwórz polecenie Services/AuthMessageSenderOptions.cs:

namespace WebPWrecover.Services;

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

Konfigurowanie wpisów tajnych użytkownika usługi SendGrid

Ustaw element SendGridKey za pomocą narzędzia secret-manager. Na przykład:

dotnet user-secrets set SendGridKey <SG.key>

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

W systemie Windows menedżer wpisów tajnych przechowuje pary kluczy/wartości w secrets.json pliku w %APPDATA%/Microsoft/UserSecrets/<WebAppName-userSecretsId> katalogu.

Zawartość secrets.json pliku nie jest szyfrowana. Poniższy znacznik pokazuje secrets.json plik. Wartość została usunięta SendGridKey .

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

Aby uzyskać więcej informacji, zobacz Wzorzec i konfiguracja opcji.

Instalowanie usługi SendGrid

W tym samouczku pokazano, jak dodawać powiadomienia e-mail za pomocą usługi SendGrid, ale możesz wysyłać wiadomości e-mail przy użyciu protokołu SMTP i innych mechanizmów.

SendGrid Zainstaluj pakiet NuGet:

W konsoli Menedżer pakietów wprowadź następujące polecenie:

Install-Package SendGrid

Zobacz Wprowadzenie do usługi SendGrid bezpłatnie , aby zarejestrować się w celu uzyskania bezpłatnego konta usługi SendGrid.

Implementowanie usługi IEmailSender

Aby zaimplementować IEmailSendermetodę , utwórz Services/EmailSender.cs za pomocą kodu podobnego do następującego:

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

Konfigurowanie uruchamiania w celu obsługi poczty e-mail

Dodaj następujący kod do ConfigureServices metody w Startup.cs pliku :

  • Dodaj EmailSender jako usługę przejściową.
  • AuthMessageSenderOptions Zarejestruj wystąpienie konfiguracji.
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();

Rejestrowanie szkieletuKonfirmacja

Postępuj zgodnie z instrukcjami dotyczącymi tworzenia szkieletów Identity i szkieletów Account\RegisterConfirmation.

Wyłącz domyślną weryfikację konta, gdy element Account.RegisterConfirmation został utworzony szkieletem

Ta sekcja ma zastosowanie tylko w przypadku Account.RegisterConfirmation tworzenia szkieletów. Pomiń tę sekcję, jeśli nie utworzono szkieletu Account.RegisterConfirmation.

Użytkownik jest przekierowywany do Account.RegisterConfirmation lokalizacji, w której może wybrać link, aby potwierdzić konto. Wartość domyślna Account.RegisterConfirmation jest używana tylko do testowania, automatyczna weryfikacja konta powinna być wyłączona w aplikacji produkcyjnej.

Aby wymagać potwierdzonego konta i uniemożliwić natychmiastowe logowanie podczas rejestracji, ustaw DisplayConfirmAccountLink = false w pliku szkieletowym /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();
        }
    }
}

Ten krok jest niezbędny tylko wtedy, gdy Account.RegisterConfirmation jest rusztowany. Niesfałdowany element RegisterConfirmation automatycznie wykrywa, kiedy element IEmailSender został zaimplementowany i zarejestrowany w kontenerze wstrzykiwania zależności.

Rejestrowanie, potwierdzanie wiadomości e-mail i resetowanie hasła

Uruchom aplikację internetową i przetestuj przepływ potwierdzenia konta i odzyskiwania hasła.

  • Uruchamianie aplikacji i rejestrowanie nowego użytkownika
  • Sprawdź adres e-mail, aby uzyskać link potwierdzenia konta. Jeśli nie otrzymasz wiadomości e-mail, zobacz Debugowanie wiadomości e-mail .
  • Kliknij link, aby potwierdzić twoją wiadomość e-mail.
  • Zaloguj się przy użyciu adresu e-mail i hasła.
  • Wyloguj się.

Testowanie resetowania hasła

  • W przypadku zalogowania wybierz pozycję Wyloguj.
  • Wybierz link Zaloguj się i wybierz link Nie pamiętasz hasła?
  • Wprowadź adres e-mail użyty do zarejestrowania konta.
  • Zostanie wysłana wiadomość e-mail z linkiem umożliwiającym zresetowanie hasła. Sprawdź swój adres e-mail i kliknij link, aby zresetować hasło. Po pomyślnym zresetowaniu hasła możesz zalogować się przy użyciu poczty e-mail i nowego hasła.

Wyślij ponownie wiadomość e-mail z potwierdzeniem

W programie ASP.NET Core 5.0 lub nowszym wybierz link Potwierdź ponownie wiadomość e-mail na stronie Logowanie .

Zmienianie limitu czasu wiadomości e-mail i działań

Domyślny limit czasu braku aktywności wynosi 14 dni. Poniższy kod ustawia limit czasu braku aktywności na 5 dni:

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

Zmienianie wszystkich cykli życia tokenu ochrony danych

Poniższy kod zmienia limit czasu wszystkich tokenów ochrony danych na 3 godziny:

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

Wbudowane Identity tokeny użytkownika (zobacz AspNetCore/src//IdentityExtensions.Core/src/TokenOptions.cs )mają jednorazowy limit czasu.

Zmienianie cyklu życia tokenu poczty e-mail

Domyślna żywotność tokenów Identity użytkownika wynosi jeden dzień. W tej sekcji pokazano, jak zmienić cykl życia tokenu poczty e-mail.

Dodaj niestandardowe DataProtectorTokenProvider<TUser> i 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);
    }
}

Dodaj dostawcę niestandardowego do kontenera usługi:

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

Debugowanie wiadomości e-mail

Jeśli nie możesz uzyskać wiadomości e-mail, działa:

  • Ustaw punkt przerwania w , EmailSender.Execute aby zweryfikować SendGridClient.SendEmailAsync , jest wywoływany.
  • Utwórz aplikację konsolową do wysyłania wiadomości e-mail przy użyciu kodu podobnego do EmailSender.Execute.
  • Przejrzyj stronę Działanie poczty e-mail.
  • Sprawdź folder spamu.
  • Wypróbuj inny alias poczty e-mail u innego dostawcy poczty e-mail (Microsoft, Yahoo, Gmail itp.)
  • Spróbuj wysłać do różnych kont e-mail.

Najlepszym rozwiązaniem w zakresie zabezpieczeń jest brak używania wpisów tajnych produkcyjnych podczas testowania i programowania. Jeśli opublikujesz aplikację na platformie Azure, ustaw wpisy tajne usługi SendGrid jako ustawienia aplikacji w portalu aplikacji internetowej platformy Azure. System konfiguracji jest skonfigurowany do odczytu kluczy ze zmiennych środowiskowych.

Łączenie kont logowania społecznościowego i lokalnego

Aby ukończyć tę sekcję, musisz najpierw włączyć zewnętrznego dostawcę uwierzytelniania. Zobacz Uwierzytelnianie za pomocą usług Facebook, Google i zewnętrznego dostawcy.

Możesz połączyć konta lokalne i społecznościowe, klikając link e-mail. W poniższej sekwencji element "RickAndMSFT@gmail.com" jest najpierw tworzony jako identyfikator logowania lokalnego, ale najpierw możesz utworzyć konto jako identyfikator logowania społecznościowego, a następnie dodać nazwę logowania lokalnego.

Aplikacja internetowa: RickAndMSFT@gmail.com uwierzytelniony użytkownik

Kliknij link Zarządzaj. Zwróć uwagę na 0 zewnętrznych (identyfikatorów logowania społecznościowego) skojarzonych z tym kontem.

Zarządzanie widokiem

Kliknij link do innej usługi logowania i zaakceptuj żądania aplikacji. Na poniższej ilustracji Facebook jest zewnętrznym dostawcą uwierzytelniania:

Zarządzanie wyświetlaniem zewnętrznych identyfikatorów logowania na facebooku

Te dwa konta zostały połączone. Możesz zalogować się przy użyciu dowolnego konta. Możesz chcieć, aby użytkownicy dodawali konta lokalne w przypadku, gdy usługa uwierzytelniania logowania społecznościowego nie działa lub prawdopodobnie utraciła dostęp do konta społecznościowego.

Włączanie potwierdzenia konta po tym, jak witryna ma użytkowników

Włączanie potwierdzenia konta w witrynie z użytkownikami blokuje wszystkich istniejących użytkowników. Istniejący użytkownicy są zablokowani, ponieważ ich konta nie są potwierdzane. Aby obejść istniejącą blokadę użytkownika, użyj jednej z następujących metod:

  • Zaktualizuj bazę danych, aby oznaczyć wszystkich istniejących użytkowników jako potwierdzonych.
  • Potwierdź istniejących użytkowników. Na przykład wiadomości e-mail wysyłane wsadowo z linkami potwierdzenia.