Einführung in Identity in ASP.NET Core

Von Rick Anderson

ASP.NET Core Identity:

  • ist eine API, die die Anmeldefunktionalität der Benutzeroberfläche (UI) unterstützt.
  • verwaltet Benutzer*innen, Kennwörter, Profildaten, Rollen, Ansprüche, Token, E-Mail-Bestätigung u. v. m.

Benutzer*innen können ein Konto mit den in Identity gespeicherten Anmeldeinformationen erstellen oder einen externen Anmeldeanbieter verwenden. Zu den unterstützten externen Anmeldeanbietern gehören Facebook, Google, Microsoft-Konto und Twitter.

Informationen dazu, wie global die Authentifizierung aller Benutzer angefordert werden kann, finden Sie unter Authentifizierte Benutzer erforderlich.

Der Quellcode von Identity steht auf GitHub zur Verfügung. Erstellen Sie ein Gerüst für Identity, und überprüfen Sie in den generierten Dateien die Interaktion der Vorlage mit Identity.

Identity wird in der Regel mit einer SQL Server-Datenbank zum Speichern von Benutzernamen, Kennwörtern und Profildaten konfiguriert. Alternativ kann ein anderer persistenter Speicher verwendet werden, z. B. Azure Table Storage.

In diesem Thema erfahren Sie, wie Sie mit Identity Benutzer*innen registrieren, anmelden und abmelden. Hinweis: In den Vorlagen werden Benutzername und E-Mail-Adresse für Benutzer*innen gleich behandelt. Ausführlichere Anweisungen zum Erstellen von Apps, die Identity verwenden, finden Sie unter Nächste Schritte.

ASP.NET Core Identity steht in keinem Zusammenhang mit Microsoft Identity Platform. Microsoft Identity Platform ist:

  • eine Weiterentwicklung der Azure AD-Entwicklerplattform (Azure Active Directory).
  • eine alternative Identitätslösung für die Authentifizierung und Autorisierung in ASP.NET Core-Apps.

ASP.NET Core Identity fügt Benutzeroberflächen-Anmeldefunktionen zu ASP.NET Core-Web-Apps hinzu. Verwenden Sie zum Sichern von Web-APIs und SPAs eine der folgenden Optionen:

Duende Identity Server ist ein OpenID Connect- und OAuth 2.0-Framework für ASP.NET Core. Duende Identity Server ermöglicht die folgenden Sicherheitsfeatures:

  • Authentifizierung als Dienst
  • Einmaliges Anmelden und einmaliges Abmelden für mehrere Anwendungstypen
  • Zugriffssteuerung für APIs
  • Federation Gateway

Wichtig

Duende Software erhebt ggf. eine Lizenzgebühr für die Nutzung von Duende Identity Server in der Produktion. Weitere Informationen finden Sie unter Migrieren von ASP.NET Core 5.0 zu 6.0.

Weitere Informationen finden Sie in der Dokumentation zu Duende Identity Server (Website von Duende Software).

Beispielcode anzeigen oder herunterladen (Vorgehensweise zum Herunterladen)

Erstellen einer Web-App mit Authentifizierung

Erstellen Sie ein Projekt für eine ASP.NET Core-Webanwendung mit einzelnen Benutzerkonten.

  • Wählen Sie die Vorlage ASP.NET Core-Web-App aus. Nennen Sie das Projekt WebApp1, um denselben Namespace wie der Projektdownload zu verwenden. Klicken Sie auf OK.
  • Wählen Sie in der Eingabe Authentifizierungstyp die Option Einzelne Benutzerkonten aus.

Das generierte Projekt stellt ASP.NET Core Identity als Razor-Klassenbibliothek bereit. Die IdentityRazor-Klassenbibliothek macht Endpunkte im Identity-Bereich verfügbar. Beispiel:

  • /Identity/Account/Login
  • /Identity/Account/Logout
  • /Identity/Account/Manage

Anwenden von Migrationen

Wenden Sie die Migrationen an, um die Datenbank zu initialisieren.

Führen Sie folgenden Befehl in der Paket-Manager-Konsole aus:

Update-Database

Test der Registrierung und Anmeldung

Führen Sie die App aus, und registrieren Sie eine*n neue*n Benutzer*in. Je nach Bildschirmgröße müssen Sie möglicherweise die Umschaltfläche für die Navigation auswählen, um die Links für das Registrieren und Anmelden anzuzeigen.

Anzeigen der Identity-Datenbank

  • Wählen Sie im Menü Ansicht den SQL Server-Objekt-Explorer (SSOX) aus.
  • Navigieren Sie zu (localdb)MSSQLLocalDB(SQL Server 13). Klicken Sie mit der rechten Maustaste auf dbo.AspNetUsers>Daten anzeigen:

Contextual menu on AspNetUsers table in SQL Server Object Explorer

Konfigurieren von Identity-Diensten

Dienste werden in Program.cs hinzugefügt. Das typische Muster besteht darin, Methoden in der folgenden Reihenfolge aufzurufen:

  1. Add{Service}
  2. builder.Services.Configure{Service}
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using WebApp1.Data;

var builder = WebApplication.CreateBuilder(args);

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

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

builder.Services.Configure<IdentityOptions>(options =>
{
    // Password settings.
    options.Password.RequireDigit = true;
    options.Password.RequireLowercase = true;
    options.Password.RequireNonAlphanumeric = true;
    options.Password.RequireUppercase = true;
    options.Password.RequiredLength = 6;
    options.Password.RequiredUniqueChars = 1;

    // Lockout settings.
    options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
    options.Lockout.MaxFailedAccessAttempts = 5;
    options.Lockout.AllowedForNewUsers = true;

    // User settings.
    options.User.AllowedUserNameCharacters =
    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
    options.User.RequireUniqueEmail = false;
});

builder.Services.ConfigureApplicationCookie(options =>
{
    // Cookie settings
    options.Cookie.HttpOnly = true;
    options.ExpireTimeSpan = TimeSpan.FromMinutes(5);

    options.LoginPath = "/Identity/Account/Login";
    options.AccessDeniedPath = "/Identity/Account/AccessDenied";
    options.SlidingExpiration = true;
});

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

Der obige Code konfiguriert Identity mit den Standardwerten für die einzelnen Optionen. Dienste werden der App über die Abhängigkeitsinjektion verfügbar gemacht.

Identity wird durch Aufrufen von UseAuthentication aktiviert. UseAuthentication fügt der Anforderungspipeline Authentifizierungsmiddleware hinzu.

Die mit der Vorlage generierte App verwendet keine Autorisierung. app.UseAuthorization ist enthalten, um sicherzustellen, dass sie in der richtigen Reihenfolge hinzugefügt wird, wenn die App um eine Autorisierung erweitert wird. UseRouting, UseAuthentication und UseAuthorization müssen in der im vorherigen Code gezeigten Reihenfolge aufgerufen werden.

Weitere Informationen zu IdentityOptions finden Sie unter IdentityOptions und Starten von Anwendungen.

Erstellen von Gerüsten für Register, Login, LogOut und RegisterConfirmation

Fügen Sie die Dateien Register, Login, LogOut und RegisterConfirmation hinzu. Befolgen Sie die Anweisungen unter Erstellen eines Gerüsts für Identitäten in einem Razor-Projekt mit Autorisierung, um den in diesem Abschnitt gezeigten Code zu generieren.

Überprüfen der Registrierung

Wenn Benutzer*innen auf der Seite Register die Schaltfläche Register (Registrieren) auswählen, wird die RegisterModel.OnPostAsync-Aktion aufgerufen. Die Benutzer*innen werden von CreateAsync(TUser) im _userManager-Objekt erstellt:

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");
    ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync())
                                          .ToList();
    if (ModelState.IsValid)
    {
        var user = new IdentityUser { UserName = Input.Email, Email = Input.Email };
        var result = await _userManager.CreateAsync(user, Input.Password);
        if (result.Succeeded)
        {
            _logger.LogInformation("User created a new account with password.");

            var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
            code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
            var callbackUrl = Url.Page(
                "/Account/ConfirmEmail",
                pageHandler: null,
                values: new { area = "Identity", userId = user.Id, code = code },
                protocol: Request.Scheme);

            await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
                $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");

            if (_userManager.Options.SignIn.RequireConfirmedAccount)
            {
                return RedirectToPage("RegisterConfirmation", 
                                      new { email = Input.Email });
            }
            else
            {
                await _signInManager.SignInAsync(user, isPersistent: false);
                return LocalRedirect(returnUrl);
            }
        }
        foreach (var error in result.Errors)
        {
            ModelState.AddModelError(string.Empty, error.Description);
        }
    }

    // If we got this far, something failed, redisplay form
    return Page();
}

Deaktivieren der Standardkontoüberprüfung

Bei den Standardvorlagen werden die Benutzer*innen zu Account.RegisterConfirmation umgeleitet, wo sie einen Link auswählen können, um das Konto bestätigen zu lassen. Die Standardeinstellung von Account.RegisterConfirmation wird nur für Tests verwendet. Die automatische Kontoüberprüfung sollte in einer Produktions-App deaktiviert werden.

Um ein bestätigtes Konto zu erzwingen und eine sofortige Anmeldung bei der Registrierung zu verhindern, legen Sie DisplayConfirmAccountLink = false in /Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs fest:

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

    public string Email { get; set; }

    public bool DisplayConfirmAccountLink { get; set; }

    public string EmailConfirmationUrl { get; set; }

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

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

Anmelden

Das Anmeldeformular wird in folgenden Fällen angezeigt:

  • Der Link Log in (Anmelden) wird ausgewählt.
  • Benutzer*innen versuchen, auf eine eingeschränkte Seite zuzugreifen, für die sie nicht autorisiert sind oder für die sie nicht vom System authentifiziert wurden.

Wenn das Formular auf der Anmeldeseite übermittelt wird, wird die OnPostAsync-Aktion aufgerufen. PasswordSignInAsync wird im _signInManager-Objekt aufgerufen.

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");

    if (ModelState.IsValid)
    {
        // This doesn't count login failures towards account lockout
        // To enable password failures to trigger account lockout, 
        // set lockoutOnFailure: true
        var result = await _signInManager.PasswordSignInAsync(Input.Email,
                           Input.Password, Input.RememberMe, lockoutOnFailure: true);
        if (result.Succeeded)
        {
            _logger.LogInformation("User logged in.");
            return LocalRedirect(returnUrl);
        }
        if (result.RequiresTwoFactor)
        {
            return RedirectToPage("./LoginWith2fa", new
            {
                ReturnUrl = returnUrl,
                RememberMe = Input.RememberMe
            });
        }
        if (result.IsLockedOut)
        {
            _logger.LogWarning("User account locked out.");
            return RedirectToPage("./Lockout");
        }
        else
        {
            ModelState.AddModelError(string.Empty, "Invalid login attempt.");
            return Page();
        }
    }

    // If we got this far, something failed, redisplay form
    return Page();
}

Informationen zum Treffen von Autorisierungsentscheidungen finden Sie unter Einführung in die Autorisierung in ASP.NET Core.

Abmelden

Der Link Log out (Abmelden) ruft die LogoutModel.OnPost-Aktion auf.

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;

namespace WebApp1.Areas.Identity.Pages.Account
{
    [AllowAnonymous]
    public class LogoutModel : PageModel
    {
        private readonly SignInManager<IdentityUser> _signInManager;
        private readonly ILogger<LogoutModel> _logger;

        public LogoutModel(SignInManager<IdentityUser> signInManager, ILogger<LogoutModel> logger)
        {
            _signInManager = signInManager;
            _logger = logger;
        }

        public void OnGet()
        {
        }

        public async Task<IActionResult> OnPost(string returnUrl = null)
        {
            await _signInManager.SignOutAsync();
            _logger.LogInformation("User logged out.");
            if (returnUrl != null)
            {
                return LocalRedirect(returnUrl);
            }
            else
            {
                return RedirectToPage();
            }
        }
    }
}

Im obigen Code muss return RedirectToPage(); eine Umleitung sein, damit der Browser eine neue Anforderung ausführt und die Identität der Benutzer*innen aktualisiert wird.

SignOutAsync löscht die in einem cookie gespeicherten Benutzeransprüche.

Die Nachverarbeitung wird in Pages/Shared/_LoginPartial.cshtml angegeben:

@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager

<ul class="navbar-nav">
@if (SignInManager.IsSignedIn(User))
{
    <li class="nav-item">
        <a  class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/Index" 
                                              title="Manage">Hello @User.Identity.Name!</a>
    </li>
    <li class="nav-item">
        <form class="form-inline" asp-area="Identity" asp-page="/Account/Logout" 
                                  asp-route-returnUrl="@Url.Page("/", new { area = "" })" 
                                  method="post" >
            <button  type="submit" class="nav-link btn btn-link text-dark">Logout</button>
        </form>
    </li>
}
else
{
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Register">Register</a>
    </li>
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Login">Login</a>
    </li>
}
</ul>

Testen von Identity

Die Standvorlagen für Webprojekte erlauben den anonymen Zugriff auf die Startseiten. Fügen Sie zum Testen von Identity[Authorize] hinzu:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;

namespace WebApp1.Pages
{
    [Authorize]
    public class PrivacyModel : PageModel
    {
        private readonly ILogger<PrivacyModel> _logger;

        public PrivacyModel(ILogger<PrivacyModel> logger)
        {
            _logger = logger;
        }

        public void OnGet()
        {
        }
    }
}

Wenn Sie angemeldet sind, melden Sie sich ab. Führen Sie die App aus, und wählen Sie den Link Privacy aus. Sie werden zur Anmeldeseite umgeleitet.

Informationen zu Identity

So erkunden Sie Identity genauer

Identity Components

Alle von Identity abhängigen NuGet-Pakete sind im freigegebenen ASP.NET Core-Framework enthalten.

Das primäre Paket für Identity ist Microsoft.AspNetCore.Identity. Dieses Paket enthält den Kernsatz von Schnittstellen für ASP.NET Core Identity und wird mit Microsoft.AspNetCore.Identity.EntityFrameworkCore eingeschlossen.

Migrieren zu ASP.NET Core Identity

Weitere Informationen und Leitfäden zum Migrieren Ihres vorhandenen Identity-Speichers finden Sie unter Migrieren der Authentifizierung und Identity.

Festlegen der Kennwortsicherheit

Unter Konfiguration finden Sie ein Beispiel, mit dem Mindestkennwortanforderungen festgelegt werden.

AddDefaultIdentity und AddIdentity

AddDefaultIdentity wurde in ASP.NET Core 2.1 eingeführt. Ein Aufruf von AddDefaultIdentity ähnelt dem Aufrufen von Folgendem:

Weitere Informationen finden Sie unter in der AddDefaultIdentity-Quelle.

Verhindern der Veröffentlichung statischer Identity-Ressourcen

Um die Veröffentlichung statischer Identity-Ressourcen (Stylesheets und JavaScript-Dateien für die Identity-Benutzeroberfläche) im Webstamm zu verhindern, fügen Sie der Projektdatei der App die folgende ResolveStaticWebAssetsInputsDependsOn-Eigenschaft und das folgende RemoveIdentityAssets-Ziel hinzu:

<PropertyGroup>
  <ResolveStaticWebAssetsInputsDependsOn>RemoveIdentityAssets</ResolveStaticWebAssetsInputsDependsOn>
</PropertyGroup>

<Target Name="RemoveIdentityAssets">
  <ItemGroup>
    <StaticWebAsset Remove="@(StaticWebAsset)" Condition="%(SourceId) == 'Microsoft.AspNetCore.Identity.UI'" />
  </ItemGroup>
</Target>

Nächste Schritte

Von Rick Anderson

ASP.NET Core Identity:

  • ist eine API, die die Anmeldefunktionalität der Benutzeroberfläche (UI) unterstützt.
  • verwaltet Benutzer*innen, Kennwörter, Profildaten, Rollen, Ansprüche, Token, E-Mail-Bestätigung u. v. m.

Benutzer*innen können ein Konto mit den in Identity gespeicherten Anmeldeinformationen erstellen oder einen externen Anmeldeanbieter verwenden. Zu den unterstützten externen Anmeldeanbietern gehören Facebook, Google, Microsoft-Konto und Twitter.

Informationen dazu, wie global die Authentifizierung aller Benutzer angefordert werden kann, finden Sie unter Authentifizierte Benutzer erforderlich.

Der Quellcode von Identity steht auf GitHub zur Verfügung. Erstellen Sie ein Gerüst für Identity, und überprüfen Sie in den generierten Dateien die Interaktion der Vorlage mit Identity.

Identity wird in der Regel mit einer SQL Server-Datenbank zum Speichern von Benutzernamen, Kennwörtern und Profildaten konfiguriert. Alternativ kann ein anderer persistenter Speicher verwendet werden, z. B. Azure Table Storage.

In diesem Thema erfahren Sie, wie Sie mit Identity Benutzer*innen registrieren, anmelden und abmelden. Hinweis: In den Vorlagen werden Benutzername und E-Mail-Adresse für Benutzer*innen gleich behandelt. Ausführlichere Anweisungen zum Erstellen von Apps, die Identity verwenden, finden Sie unter Nächste Schritte.

Microsoft Identity Platform ist:

  • eine Weiterentwicklung der Azure AD-Entwicklerplattform (Azure Active Directory).
  • eine alternative Identitätslösung für die Authentifizierung und Autorisierung in ASP.NET Core-Apps.
  • nicht mit ASP.NET Core Identity verwandt.

ASP.NET Core Identity fügt Benutzeroberflächen-Anmeldefunktionen zu ASP.NET Core-Web-Apps hinzu. Verwenden Sie zum Sichern von Web-APIs und SPAs eine der folgenden Optionen:

Duende IdentityServer ist ein OpenID Connect- und OAuth 2.0-Framework für ASP.NET Core. Duende IdentityServer ermöglicht die folgenden Sicherheitsfeatures:

  • Authentifizierung als Dienst
  • Einmaliges Anmelden und einmaliges Abmelden für mehrere Anwendungstypen
  • Zugriffssteuerung für APIs
  • Federation Gateway

Weitere Informationen finden Sie in der Übersicht über Duende IdentityServer.

Weitere Informationen zu anderen Authentifizierungsanbietern finden Sie unter Community-basierte OSS-Authentifizierungsoptionen für ASP.NET Core.

Beispielcode anzeigen oder herunterladen (Vorgehensweise zum Herunterladen)

Erstellen einer Web-App mit Authentifizierung

Erstellen Sie ein Projekt für eine ASP.NET Core-Webanwendung mit einzelnen Benutzerkonten.

  • Wählen Sie Datei>Neu>Projekt aus.
  • Wählen Sie ASP.NET Core-Webanwendung aus. Nennen Sie das Projekt WebApp1, um denselben Namespace wie der Projektdownload zu verwenden. Klicken Sie auf OK.
  • Wählen Sie eine ASP.NET Core-Webanwendung und dann Authentifizierung ändern aus.
  • Wählen Sie Einzelne Benutzerkonten und dann OK aus.

Das generierte Projekt stellt ASP.NET Core Identity als Razor-Klassenbibliothek bereit. Die IdentityRazor-Klassenbibliothek macht Endpunkte im Identity-Bereich verfügbar. Beispiel:

  • /Identity/Account/Login
  • /Identity/Account/Logout
  • /Identity/Account/Manage

Anwenden von Migrationen

Wenden Sie die Migrationen an, um die Datenbank zu initialisieren.

Führen Sie folgenden Befehl in der Paket-Manager-Konsole aus:

PM> Update-Database

Test der Registrierung und Anmeldung

Führen Sie die App aus, und registrieren Sie eine*n neue*n Benutzer*in. Je nach Bildschirmgröße müssen Sie möglicherweise die Umschaltfläche für die Navigation auswählen, um die Links für das Registrieren und Anmelden anzuzeigen.

Anzeigen der Identity-Datenbank

  • Wählen Sie im Menü Ansicht den SQL Server-Objekt-Explorer (SSOX) aus.
  • Navigieren Sie zu (localdb)MSSQLLocalDB(SQL Server 13). Klicken Sie mit der rechten Maustaste auf dbo.AspNetUsers>Daten anzeigen:

Contextual menu on AspNetUsers table in SQL Server Object Explorer

Konfigurieren von Identity-Diensten

Dienste werden in ConfigureServices hinzugefügt. Das typische Muster besteht darin, alle Add{Service}-Methoden und dann alle services.Configure{Service}-Methoden aufzurufen.

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
     // options.UseSqlite(
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();
    services.AddRazorPages();

    services.Configure<IdentityOptions>(options =>
    {
        // Password settings.
        options.Password.RequireDigit = true;
        options.Password.RequireLowercase = true;
        options.Password.RequireNonAlphanumeric = true;
        options.Password.RequireUppercase = true;
        options.Password.RequiredLength = 6;
        options.Password.RequiredUniqueChars = 1;

        // Lockout settings.
        options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
        options.Lockout.MaxFailedAccessAttempts = 5;
        options.Lockout.AllowedForNewUsers = true;

        // User settings.
        options.User.AllowedUserNameCharacters =
        "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
        options.User.RequireUniqueEmail = false;
    });

    services.ConfigureApplicationCookie(options =>
    {
        // Cookie settings
        options.Cookie.HttpOnly = true;
        options.ExpireTimeSpan = TimeSpan.FromMinutes(5);

        options.LoginPath = "/Identity/Account/Login";
        options.AccessDeniedPath = "/Identity/Account/AccessDenied";
        options.SlidingExpiration = true;
    });
}

Der obige hervorgehobene Code konfiguriert Identity mit den Standardwerten für die einzelnen Optionen. Dienste werden der App über die Abhängigkeitsinjektion verfügbar gemacht.

Identity wird durch Aufrufen von UseAuthentication aktiviert. UseAuthentication fügt der Anforderungspipeline Authentifizierungsmiddleware hinzu.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

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

    app.UseRouting();

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

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}
public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        // options.UseSqlite(
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDatabaseDeveloperPageExceptionFilter();
    services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();
    services.AddRazorPages();

    services.Configure<IdentityOptions>(options =>
    {
        // Password settings.
        options.Password.RequireDigit = true;
        options.Password.RequireLowercase = true;
        options.Password.RequireNonAlphanumeric = true;
        options.Password.RequireUppercase = true;
        options.Password.RequiredLength = 6;
        options.Password.RequiredUniqueChars = 1;

        // Lockout settings.
        options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
        options.Lockout.MaxFailedAccessAttempts = 5;
        options.Lockout.AllowedForNewUsers = true;

        // User settings.
        options.User.AllowedUserNameCharacters =
        "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
        options.User.RequireUniqueEmail = false;
    });

    services.ConfigureApplicationCookie(options =>
    {
        // Cookie settings
        options.Cookie.HttpOnly = true;
        options.ExpireTimeSpan = TimeSpan.FromMinutes(5);

        options.LoginPath = "/Identity/Account/Login";
        options.AccessDeniedPath = "/Identity/Account/AccessDenied";
        options.SlidingExpiration = true;
    });
}

Der obige Code konfiguriert Identity mit den Standardwerten für die einzelnen Optionen. Dienste werden der App über die Abhängigkeitsinjektion verfügbar gemacht.

Identity wird durch Aufrufen von UseAuthentication aktiviert. UseAuthentication fügt der Anforderungspipeline Authentifizierungsmiddleware hinzu.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseMigrationsEndPoint();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

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

    app.UseRouting();

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

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

Die mit der Vorlage generierte App verwendet keine Autorisierung. app.UseAuthorization ist enthalten, um sicherzustellen, dass sie in der richtigen Reihenfolge hinzugefügt wird, wenn die App um eine Autorisierung erweitert wird. UseRouting, UseAuthentication, UseAuthorization und UseEndpoints müssen in der im vorherigen Code gezeigten Reihenfolge aufgerufen werden.

Weitere Informationen zu IdentityOptions und Startup finden Sie unter IdentityOptions und Starten von Anwendungen.

Erstellen von Gerüsten für Register, Login, LogOut und RegisterConfirmation

Fügen Sie die Dateien Register, Login, LogOut und RegisterConfirmation hinzu. Befolgen Sie die Anweisungen unter Erstellen eines Gerüsts für Identitäten in einem Razor-Projekt mit Autorisierung, um den in diesem Abschnitt gezeigten Code zu generieren.

Überprüfen der Registrierung

Wenn Benutzer*innen auf der Seite Register die Schaltfläche Register (Registrieren) auswählen, wird die RegisterModel.OnPostAsync-Aktion aufgerufen. Die Benutzer*innen werden von CreateAsync(TUser) im _userManager-Objekt erstellt:

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");
    ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync())
                                          .ToList();
    if (ModelState.IsValid)
    {
        var user = new IdentityUser { UserName = Input.Email, Email = Input.Email };
        var result = await _userManager.CreateAsync(user, Input.Password);
        if (result.Succeeded)
        {
            _logger.LogInformation("User created a new account with password.");

            var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
            code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
            var callbackUrl = Url.Page(
                "/Account/ConfirmEmail",
                pageHandler: null,
                values: new { area = "Identity", userId = user.Id, code = code },
                protocol: Request.Scheme);

            await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
                $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");

            if (_userManager.Options.SignIn.RequireConfirmedAccount)
            {
                return RedirectToPage("RegisterConfirmation", 
                                      new { email = Input.Email });
            }
            else
            {
                await _signInManager.SignInAsync(user, isPersistent: false);
                return LocalRedirect(returnUrl);
            }
        }
        foreach (var error in result.Errors)
        {
            ModelState.AddModelError(string.Empty, error.Description);
        }
    }

    // If we got this far, something failed, redisplay form
    return Page();
}

Deaktivieren der Standardkontoüberprüfung

Bei den Standardvorlagen werden die Benutzer*innen zu Account.RegisterConfirmation umgeleitet, wo sie einen Link auswählen können, um das Konto bestätigen zu lassen. Die Standardeinstellung von Account.RegisterConfirmation wird nur für Tests verwendet. Die automatische Kontoüberprüfung sollte in einer Produktions-App deaktiviert werden.

Um ein bestätigtes Konto zu erzwingen und eine sofortige Anmeldung bei der Registrierung zu verhindern, legen Sie DisplayConfirmAccountLink = false in /Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs fest:

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

    public string Email { get; set; }

    public bool DisplayConfirmAccountLink { get; set; }

    public string EmailConfirmationUrl { get; set; }

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

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

Anmelden

Das Anmeldeformular wird in folgenden Fällen angezeigt:

  • Der Link Log in (Anmelden) wird ausgewählt.
  • Benutzer*innen versuchen, auf eine eingeschränkte Seite zuzugreifen, für die sie nicht autorisiert sind oder für die sie nicht vom System authentifiziert wurden.

Wenn das Formular auf der Anmeldeseite übermittelt wird, wird die OnPostAsync-Aktion aufgerufen. PasswordSignInAsync wird im _signInManager-Objekt aufgerufen.

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");

    if (ModelState.IsValid)
    {
        // This doesn't count login failures towards account lockout
        // To enable password failures to trigger account lockout, 
        // set lockoutOnFailure: true
        var result = await _signInManager.PasswordSignInAsync(Input.Email,
                           Input.Password, Input.RememberMe, lockoutOnFailure: true);
        if (result.Succeeded)
        {
            _logger.LogInformation("User logged in.");
            return LocalRedirect(returnUrl);
        }
        if (result.RequiresTwoFactor)
        {
            return RedirectToPage("./LoginWith2fa", new
            {
                ReturnUrl = returnUrl,
                RememberMe = Input.RememberMe
            });
        }
        if (result.IsLockedOut)
        {
            _logger.LogWarning("User account locked out.");
            return RedirectToPage("./Lockout");
        }
        else
        {
            ModelState.AddModelError(string.Empty, "Invalid login attempt.");
            return Page();
        }
    }

    // If we got this far, something failed, redisplay form
    return Page();
}

Informationen zum Treffen von Autorisierungsentscheidungen finden Sie unter Einführung in die Autorisierung in ASP.NET Core.

Abmelden

Der Link Log out (Abmelden) ruft die LogoutModel.OnPost-Aktion auf.

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;

namespace WebApp1.Areas.Identity.Pages.Account
{
    [AllowAnonymous]
    public class LogoutModel : PageModel
    {
        private readonly SignInManager<IdentityUser> _signInManager;
        private readonly ILogger<LogoutModel> _logger;

        public LogoutModel(SignInManager<IdentityUser> signInManager, ILogger<LogoutModel> logger)
        {
            _signInManager = signInManager;
            _logger = logger;
        }

        public void OnGet()
        {
        }

        public async Task<IActionResult> OnPost(string returnUrl = null)
        {
            await _signInManager.SignOutAsync();
            _logger.LogInformation("User logged out.");
            if (returnUrl != null)
            {
                return LocalRedirect(returnUrl);
            }
            else
            {
                return RedirectToPage();
            }
        }
    }
}

Im obigen Code muss return RedirectToPage(); eine Umleitung sein, damit der Browser eine neue Anforderung ausführt und die Identität der Benutzer*innen aktualisiert wird.

SignOutAsync löscht die in einem cookie gespeicherten Benutzeransprüche.

Die Nachverarbeitung wird in Pages/Shared/_LoginPartial.cshtml angegeben:

@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager

<ul class="navbar-nav">
@if (SignInManager.IsSignedIn(User))
{
    <li class="nav-item">
        <a  class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/Index" 
                                              title="Manage">Hello @User.Identity.Name!</a>
    </li>
    <li class="nav-item">
        <form class="form-inline" asp-area="Identity" asp-page="/Account/Logout" 
                                  asp-route-returnUrl="@Url.Page("/", new { area = "" })" 
                                  method="post" >
            <button  type="submit" class="nav-link btn btn-link text-dark">Logout</button>
        </form>
    </li>
}
else
{
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Register">Register</a>
    </li>
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Login">Login</a>
    </li>
}
</ul>

Testen von Identity

Die Standvorlagen für Webprojekte erlauben den anonymen Zugriff auf die Startseiten. Fügen Sie zum Testen von Identity[Authorize] hinzu:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;

namespace WebApp1.Pages
{
    [Authorize]
    public class PrivacyModel : PageModel
    {
        private readonly ILogger<PrivacyModel> _logger;

        public PrivacyModel(ILogger<PrivacyModel> logger)
        {
            _logger = logger;
        }

        public void OnGet()
        {
        }
    }
}

Wenn Sie angemeldet sind, melden Sie sich ab. Führen Sie die App aus, und wählen Sie den Link Privacy aus. Sie werden zur Anmeldeseite umgeleitet.

Informationen zu Identity

So erkunden Sie Identity genauer

Identity Components

Alle von Identity abhängigen NuGet-Pakete sind im freigegebenen ASP.NET Core-Framework enthalten.

Das primäre Paket für Identity ist Microsoft.AspNetCore.Identity. Dieses Paket enthält den Kernsatz von Schnittstellen für ASP.NET Core Identity und wird mit Microsoft.AspNetCore.Identity.EntityFrameworkCore eingeschlossen.

Migrieren zu ASP.NET Core Identity

Weitere Informationen und Leitfäden zum Migrieren Ihres vorhandenen Identity-Speichers finden Sie unter Migrieren der Authentifizierung und Identity.

Festlegen der Kennwortsicherheit

Unter Konfiguration finden Sie ein Beispiel, mit dem Mindestkennwortanforderungen festgelegt werden.

Verhindern der Veröffentlichung statischer Identity-Ressourcen

Um die Veröffentlichung statischer Identity-Ressourcen (Stylesheets und JavaScript-Dateien für die Identity-Benutzeroberfläche) im Webstamm zu verhindern, fügen Sie der Projektdatei der App die folgende ResolveStaticWebAssetsInputsDependsOn-Eigenschaft und das folgende RemoveIdentityAssets-Ziel hinzu:

<PropertyGroup>
  <ResolveStaticWebAssetsInputsDependsOn>RemoveIdentityAssets</ResolveStaticWebAssetsInputsDependsOn>
</PropertyGroup>

<Target Name="RemoveIdentityAssets">
  <ItemGroup>
    <StaticWebAsset Remove="@(StaticWebAsset)" Condition="%(SourceId) == 'Microsoft.AspNetCore.Identity.UI'" />
  </ItemGroup>
</Target>

Nächste Schritte