Sichern einer gehosteten Blazor WebAssembly-App in ASP.NET Core mit Identity Server

In diesem Artikel wird erläutert, wie eine gehostete Blazor WebAssembly-Lösung erstellt wird, die Duende Identity Server verwendet, um Benutzer und API-Aufrufe zu authentifizieren.

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.

Hinweis

Um eine eigenständige oder gehostete Blazor WebAssembly-App für die Verwendung einer vorhandenen, externen Identity Server-Instanz zu konfigurieren, folgen Sie der Anleitung in Sichern einer eigenständigen ASP.NET Core Blazor WebAssembly-App mit der Authentifizierungsbibliothek.

Nach dem Lesen dieses Artikels finden Sie weitere Informationen zu Sicherheitsszenarien unter Zusätzliche Sicherheitsszenarios für ASP.NET Core Blazor WebAssembly.

Exemplarische Vorgehensweise

In den Unterabschnitten der exemplarischen Vorgehensweise wird Folgendes erläutert:

  • Erstellen der Blazor-App
  • Ausführen der App

Erstellen einer Blazor-App

So erstellen Sie ein neues Blazor WebAssembly-Projekt mit einem Authentifizierungsmechanismus:

  1. Erstellen Sie ein neues Projekt.

  2. Wählen Sie die Blazor WebAssembly App-Vorlage aus. Klicken Sie auf Weiter.

  3. Geben Sie einen Projektnamen ohne Bindestriche an. Überprüfen Sie die Richtigkeit des Speicherorts. Wählen Sie Weiter aus.

    Verwenden Sie keine Bindestriche (-) im Projektnamen, da die Formatierung des OIDC-App-Bezeichners dadurch beeinträchtigt wird. Die Logik in der Blazor WebAssembly-Projektvorlage verwendet den Projektnamen für einen OIDC-App-Bezeichner in der Konfiguration der Projektmappe, und Bindestriche sind in einem OIDC-App-Bezeichner nicht zulässig. Die Pascal-Schreibweise (BlazorSample) oder Unterstriche (Blazor_Sample) stellen akzeptable Alternativen dar.

  4. Wählen Sie im Dialogfeld Zusätzliche Informationen die Option Einzelne Konten als Authentifizierungstyp aus, um Benutzer mit dem Identity-System von ASP.NET Core zu speichern.

  5. Aktivieren Sie das Kontrollkästchen ASP.NET Core, gehostet.

  6. Wählen Sie die Schaltfläche Erstellen aus, um die App zu erstellen.

Ausführen der App

Führen Sie die App aus dem Projekt Server heraus aus. Wenn Sie Visual Studio verwenden, haben Sie folgende beiden Möglichkeiten:

  • Wählen Sie den Dropdownpfeil neben der Schaltfläche Ausführen aus. Öffnen Sie in der Dropdownliste Startprojekte konfigurieren. Wählen Sie die Option Einzelnes Startprojekt aus. Bestätigen oder ändern Sie das Projekt für das Startprojekt in Server-Projekt.

  • Vergewissern Sie sich, dass das Projekt Server im Projektmappen-Explorer hervorgehoben ist, bevor Sie die App mit einem der folgenden Ansätze starten:

    • Wählen Sie die Schaltfläche Ausführen.
    • Verwenden Sie im Menü Debuggen>Debuggen starten.
    • Drücken Sie F5.
  • Navigieren Sie in einer Befehlsshell zum Projektordner Server der Projektmappe. Führen Sie den dotnet run-Befehl aus.

Bestandteile der Lösung

In diesem Abschnitt wird erläutert, welche Teile einer Projektmappe aus der Blazor WebAssembly-Projektvorlage generiert werden und wie die Client- und Server-Projekte der Projektmappe konfiguriert werden. Es gibt in diesem Abschnitt keine speziellen Anleitungen für eine grundlegende funktionierende Anwendung, wenn Sie die App mithilfe der Anweisungen im Abschnitt Vorgehensweise erstellt haben. Die Anweisungen in diesem Abschnitt sind hilfreich, um die App mit Features zur Authentifizierung und Autorisierung von Benutzer*innen zu ergänzen. Eine alternativer Ansatz zum Aktualisieren einer App besteht darin, eine neue App anhand der Anweisungen im Abschnitt Vorgehensweise zu erstellen und die Komponenten, Klassen und Ressourcen der App in die neue App zu verschieben.

Server App-Dienste

Dieser Abschnitt bezieht sich auf die Server -App der Lösung.

Die folgenden Dienste werden registriert.

  • In der Program-Datei:

    • Entity Framework Core und ASP.NET Core Identity:

      builder.Services.AddDbContext<ApplicationDbContext>(options =>
          options.UseSqlite( ... ));
      builder.Services.AddDatabaseDeveloperPageExceptionFilter();
      
      builder.Services.AddDefaultIdentity<ApplicationUser>(options => 
              options.SignIn.RequireConfirmedAccount = true)
          .AddEntityFrameworkStores<ApplicationDbContext>();
      
    • Identity Server mit einer zusätzlichen AddApiAuthorization-Hilfsmethode, die ASP.NET Core-Standardkonventionen zusätzlich zu Identity Server einrichtet:

      builder.Services.AddIdentityServer()
          .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
      
    • Authentifizierung mit einer zusätzlichen AddIdentityServerJwt-Hilfsmethode, die die App so konfiguriert, dass sie von Identity Server generierte JWT-Token überprüft:

      builder.Services.AddAuthentication()
          .AddIdentityServerJwt();
      
  • In Startup.ConfigureServices von Startup.cs:

    • Entity Framework Core und ASP.NET Core Identity:

      services.AddDbContext<ApplicationDbContext>(options =>
          options.UseSqlite(
              Configuration.GetConnectionString("DefaultConnection")));
      
      services.AddDefaultIdentity<ApplicationUser>(options => 
              options.SignIn.RequireConfirmedAccount = true)
          .AddEntityFrameworkStores<ApplicationDbContext>();
      
    • Identity Server mit einer zusätzlichen AddApiAuthorization-Hilfsmethode, die ASP.NET Core-Standardkonventionen zusätzlich zu Identity Server einrichtet:

      services.AddIdentityServer()
          .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
      
    • Authentifizierung mit einer zusätzlichen AddIdentityServerJwt-Hilfsmethode, die die App so konfiguriert, dass sie von Identity Server generierte JWT-Token überprüft:

      services.AddAuthentication()
          .AddIdentityServerJwt();
      

Hinweis

Wenn ein einzelnes Authentifizierungsschema registriert ist, wird es automatisch als Standardschema der App verwendet, und es ist nicht erforderlich, das Schema auf AddAuthentication oder über AuthenticationOptions anzugeben. Weitere Informationen finden Sie in der Übersicht über die ASP.NET Core- Authentifizierung und in der ASP.NET Core-Ankündigung (aspnet/Announcements #490).

  • Gehen Sie in der Program-Datei folgendermaßen vor:
  • In Startup.Configure von Startup.cs:
  • Die Identity Server-Middleware stellt die Open ID Connect-Endpunkte (OIDC) bereit:

    app.UseIdentityServer();
    
  • Die Authentifizierungsmiddleware ist für die Überprüfung der Anforderungsanmeldeinformationen und für das Festlegen des Benutzers auf den Anforderungskontext verantwortlich:

    app.UseAuthentication();
    
  • Die Autorisierungsmiddleware aktiviert Autorisierungsfunktionen:

    app.UseAuthorization();
    

API-Autorisierung

Dieser Abschnitt bezieht sich auf die Server -App der Lösung.

Die AddApiAuthorization-Hilfsmethode konfiguriert Identity Server für ASP.NET Core-Szenarios. Identity Server ist ein leistungsfähiges und erweiterbares Framework für die Umsetzung der App-Sicherheit. Identity Server ist für die meisten gängigen Szenarien unnötig komplex. Als Folge werden mehrere Konventionen und Konfigurationsoptionen bereitgestellt, die sich gut als Startpunkt eignen. Wenn sich Ihre Anforderungen an die Authentifizierung ändern, bietet Identity Server eine Vielzahl leistungsfähiger Funktionen, mit denen Sie die Authentifizierung genau an die Anforderungen einer App anpassen können.

Hinzufügen eines Authentifizierungshandlers für eine API, die zusammen mit Identity Server vorhanden ist

Dieser Abschnitt bezieht sich auf die Server -App der Lösung.

Die AddIdentityServerJwt-Hilfsprogrammmethode konfiguriert ein Richtlinienschema für die App als Standardauthentifizierungshandler. Die Richtlinie ist so konfiguriert, dass Identity alle an beliebige Unterpfade im Identity-URL-Raum unter /Identity weitergeleiteten Anforderungen verarbeiten kann. JwtBearerHandler verarbeitet alle anderen Anforderungen. Außerdem übernimmt diese Methode die folgenden Aufgaben:

  • Registriert eine API-Ressource bei Identity Server mit dem Standardbereich von {PROJECT NAME}API, wobei der Platzhalter {PROJECT NAME} der Name des Projekts bei der App-Erstellung ist.
  • Konfigurieren der JWT-Bearertoken-Middleware zum Überprüfen der von Identity Server für die App ausgegebenen Token.

Wettervorhersagecontroller

Dieser Abschnitt bezieht sich auf die Server -App der Lösung.

In WeatherForecastController (Controllers/WeatherForecastController.cs) wird das [Authorize]-Attribut auf die Klasse angewendet. Das Attribut gibt an, dass der Benutzer basierend auf der Standardrichtlinie autorisiert werden muss, um auf die Ressource zuzugreifen. Die Standardautorisierungsrichtlinie ist so konfiguriert, dass das Standardauthentifizierungsschema verwendet wird, das von AddIdentityServerJwt eingerichtet wird. Die Hilfsprogrammmethode konfiguriert JwtBearerHandler als Standardhandler für Anforderungen an die App.

Anwendungsdatenbankkontext

Dieser Abschnitt bezieht sich auf die Server -App der Lösung.

In ApplicationDbContext (Data/ApplicationDbContext.cs) wird ApiAuthorizationDbContext<TUser> durch DbContext erweitert, damit das Schema für Identity Server eingeschlossen wird. ApiAuthorizationDbContext<TUser> wird von IdentityDbContext abgeleitet.

Für eine vollständige Kontrolle des Datenbankschemas wird von einer der verfügbaren Identity-DbContext-Klassen geerbt, und der Kontext wird so konfiguriert, dass er das Identity-Schema einschließt, indem builder.ConfigurePersistedGrantContext(_operationalStoreOptions.Value) in der OnModelCreating-Methode aufgerufen wird.

OIDC-Konfigurationscontroller

Dieser Abschnitt bezieht sich auf die Server -App der Lösung.

In OidcConfigurationController (Controllers/OidcConfigurationController.cs) wird der Clientendpunkt bereitgestellt, um OIDC-Parameter bereitzustellen.

App-Einstellungen

Dieser Abschnitt bezieht sich auf die Server -App der Lösung.

In der App-Einstellungsdatei (appsettings.json) beschreibt der Bereich IdentityServer auf Projektstammebene die Liste der konfigurierten Clients. Im folgenden Beispiel gibt es einen einzelnen Client. Der Clientname entspricht dem Client-App-Namen, und er wird durch Konventionen dem OAuth-Parameter ClientId zugeordnet. Das Profil gibt den App-Typ an, der konfiguriert wird. Das Profil wird intern verwendet, um Konventionen zu etablieren, die den Konfigurationsprozess für den Server vereinfachen.

"IdentityServer": {
  "Clients": {
    "{ASSEMBLY NAME}": {
      "Profile": "IdentityServerSPA"
    }
  }
}

Der Platzhalter {ASSEMBLY NAME} ist der Assemblyname der Client-App (z. B. BlazorSample.Client).

Authentifizierungspaket

Dieser Abschnitt bezieht sich auf die Client -App der Lösung.

Wenn eine App erstellt wird, die einzelne Benutzerkonten (Individual) verwendet, erhält die App automatisch einen Paketverweis für das Microsoft.AspNetCore.Components.WebAssembly.Authentication-Paket. Das Paket stellt einige Primitive bereit, die der App beim Authentifizieren von Benutzern und beim Abrufen von Token zum Aufrufen geschützter APIs helfen.

Wenn Sie einer App eine Authentifizierung hinzufügen, fügen Sie das Paket Microsoft.AspNetCore.Components.WebAssembly.Authentication der App manuell hinzu:

Hinweis

Einen Leitfaden zum Hinzufügen von Paketen zu .NET-Apps finden Sie in Installieren und Verwalten von Paketen unter Workflow der Nutzung von Paketen (NuGet-Dokumentation). Überprüfen Sie unter NuGet.org, ob die richtige Paketversion verwendet wird.

HttpClient-Konfiguration

Dieser Abschnitt bezieht sich auf die Client -App der Lösung.

In der Program Datei ist ein benannter HttpClient konfiguriert, umHttpClient-Instanzen bereitzustellen, die bei Anforderungen an die Server-API Zugriffstoken einschließen. Bei der Erstellung der Projektmappe lautet der Name benannte HttpClient standardmäßig {PROJECT NAME}.ServerAPI, wobei der Platzhalter {PROJECT NAME} der Name des Projekts ist.

builder.Services.AddHttpClient("{PROJECT NAME}.ServerAPI", 
        client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
    .AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();

builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>()
    .CreateClient("{PROJECT NAME}.ServerAPI"));

Der Platzhalter {PROJECT NAME} ist der Projektname bei der Projektmappenerstellung. Wenn Sie z. B. einen Projektnamen von BlazorSample angeben, wird ein benannter HttpClient von BlazorSample.ServerAPI erzeugt.

Hinweis

Wenn Sie eine Blazor WebAssembly-App für die Verwendung einer vorhandenen Identity Server-Instanz konfigurieren, die nicht Teil einer gehosteten Blazor-Lösung ist, ändern Sie die Registrierung der HttpClient-Basisadresse von IWebAssemblyHostEnvironment.BaseAddress (builder.HostEnvironment.BaseAddress) in die URL des API-Autorisierungsendpunkts der Server-App.

API-Autorisierungsunterstützung

Dieser Abschnitt bezieht sich auf die Client -App der Lösung.

Die Unterstützung für die Authentifizierung von Benutzern wird im Dienstcontainer von der im Microsoft.AspNetCore.Components.WebAssembly.Authentication-Paket bereitgestellten -Erweiterungsmethode eingefügt. Diese Methode richtet die Dienste ein, die erforderlich sind, damit die App mit dem vorhandenen Autorisierungssystem interagiert.

builder.Services.AddApiAuthorization();

Standardmäßig wird die Konfiguration für die App durch die Konvention von _configuration/{client-id} geladen. Per Konvention wird die Client-ID auf den Assemblynamen der App festgelegt. Diese URL kann so geändert werden, dass sie auf einen eigenen Endpunkt verweist, indem das Überladen mit Optionen aufgerufen wird.

Imports-Datei

Dieser Abschnitt bezieht sich auf die Client -App der Lösung.

Der Microsoft.AspNetCore.Components.Authorization-Namespace wird in der gesamten App über die _Imports.razor-Datei verfügbar gemacht:

@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.AspNetCore.Components.WebAssembly.Http
@using Microsoft.JSInterop
@using {APPLICATION ASSEMBLY}
@using {APPLICATION ASSEMBLY}.Shared

Index-Seite

Dieser Abschnitt bezieht sich auf die Client -App der Lösung.

Die Indexseite (wwwroot/index.html) enthält ein Skript, das den AuthenticationService in JavaScript definiert. AuthenticationService behandelt die Details des OIDC-Protokolls auf niedriger Ebene. Die App ruft intern die im Skript definierten Methoden auf, um die Authentifizierungsvorgänge auszuführen.

<script src="_content/Microsoft.AspNetCore.Components.WebAssembly.Authentication/AuthenticationService.js"></script>

App-Komponente

Dieser Abschnitt bezieht sich auf die Client -App der Lösung.

Die App-Komponente (App.razor) ähnelt der in den Blazor Server-Apps gefundenen App-Komponente:

  • Die CascadingAuthenticationState-Komponente verwaltet das Verfügbarmachen der AuthenticationState-Komponente für die restliche App.
  • Die AuthorizeRouteView-Komponente stellt sicher, dass der aktuelle Benutzer autorisiert ist, auf eine bestimmte Seite zuzugreifen; andernfalls wird die RedirectToLogin-Komponente gerendert.
  • Die RedirectToLogin-Komponente verwaltet die Umleitung nicht autorisierter Benutzer auf die Anmeldeseite.

Aufgrund von Änderungen im Framework in den verschiedenen Releases von ASP.NET Core wird in diesem Abschnitt kein Razor-Markup für die Komponente App (App.razor) angezeigt. Wenn Sie das Markup der Komponente für ein bestimmtes Release überprüfen möchten, verwenden Sie einen der folgenden Ansätze:

  • Erstellen Sie eine für die Authentifizierung bereitgestellte App aus der Blazor WebAssembly-Standardprojektvorlage für die Version von ASP.NET Core, die Sie verwenden möchten. Untersuchen Sie die App-Komponente (App.razor) in der generierten App.

  • Untersuchen Sie die App-Komponente (App.razor) in der Verweisquelle. Wählen Sie die Version aus der Verzweigungsselektor aus, und suchen Sie nach der Komponente im ProjectTemplates-Ordner des Repositorys, da sie im Laufe der Jahre verschoben wurde.

    Hinweis

    Dokumentationslinks zur .NET-Referenzquelle laden in der Regel den Standardbranch des Repositorys, der die aktuelle Entwicklung für das nächste Release von .NET darstellt. Um ein Tag für ein bestimmtes Release auszuwählen, wählen Sie diesen mit der Dropdownliste Switch branches or tags (Branches oder Tags wechseln) aus. Weitere Informationen finden Sie unter How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Auswählen eines Versionstags von ASP.NET Core-Quellcode (dotnet/AspNetCore.Docs #26205)).

RedirectToLogin-Komponente

Dieser Abschnitt bezieht sich auf die Client -App der Lösung.

Die RedirectToLogin-Komponente (RedirectToLogin.razor):

  • Verwaltet die Umleitung nicht autorisierter Benutzer auf die Anmeldeseite.
  • Die aktuelle URL, auf die der Benutzer zuzugreifen versucht, wird beibehalten, damit er zu dieser Seite zurückkehren kann, wenn die Authentifizierung erfolgreich Folgendes verwendet:
    • Navigationsverlaufsstatus in ASP.NET Core in .NET 7 oder höher.
    • Eine Abfragezeichenfolge in ASP.NET Core in .NET 6 oder früher.

Untersuchen Sie die RedirectToLogin-Komponente in der Verweisquelle. Der Speicherort der Komponente wurde im Laufe der Zeit geändert. Verwenden Sie daher GitHub-Suchtools, um die Komponente zu finden.

Hinweis

Dokumentationslinks zur .NET-Referenzquelle laden in der Regel den Standardbranch des Repositorys, der die aktuelle Entwicklung für das nächste Release von .NET darstellt. Um ein Tag für ein bestimmtes Release auszuwählen, wählen Sie diesen mit der Dropdownliste Switch branches or tags (Branches oder Tags wechseln) aus. Weitere Informationen finden Sie unter How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Auswählen eines Versionstags von ASP.NET Core-Quellcode (dotnet/AspNetCore.Docs #26205)).

LoginDisplay-Komponente

Dieser Abschnitt bezieht sich auf die Client -App der Lösung.

Die LoginDisplay-Komponente (LoginDisplay.razor) wird in der MainLayout-Komponente (MainLayout.razor) gerendert, und sie verwaltet die folgenden Verhaltensweisen:

  • Für authentifizierte Benutzer:
    • Zeigt den aktuellen Benutzernamen an
    • Stellt einen Link zur Benutzerprofilseite in ASP.NET Core-Identity bereit
    • Bietet eine Schaltfläche zum Abmelden von der App
  • Für anonyme Benutzer:
    • Bietet die Option zum Registrieren
    • Bietet die Option zur Anmeldung

Aufgrund von Änderungen im Framework in den verschiedenen Releases von ASP.NET Core wird in diesem Abschnitt kein Razor-Markup für die Komponente LoginDisplay angezeigt. Wenn Sie das Markup der Komponente für ein bestimmtes Release überprüfen möchten, verwenden Sie einen der folgenden Ansätze:

  • Erstellen Sie eine für die Authentifizierung bereitgestellte App aus der Blazor WebAssembly-Standardprojektvorlage für die Version von ASP.NET Core, die Sie verwenden möchten. Überprüfen Sie die Komponente LoginDisplay in der generierten App.

  • Überprüfen Sie die Komponente LoginDisplay in der Verweisquelle. Der Speicherort der Komponente wurde im Laufe der Zeit geändert. Verwenden Sie daher GitHub-Suchtools, um die Komponente zu finden. Es wird der vorlagenbasierte Inhalt für Hosted gleich true verwendet.

    Hinweis

    Dokumentationslinks zur .NET-Referenzquelle laden in der Regel den Standardbranch des Repositorys, der die aktuelle Entwicklung für das nächste Release von .NET darstellt. Um ein Tag für ein bestimmtes Release auszuwählen, wählen Sie diesen mit der Dropdownliste Switch branches or tags (Branches oder Tags wechseln) aus. Weitere Informationen finden Sie unter How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Auswählen eines Versionstags von ASP.NET Core-Quellcode (dotnet/AspNetCore.Docs #26205)).

Authentication-Komponente

Dieser Abschnitt bezieht sich auf die Client -App der Lösung.

Die von der Komponente Authentication (Pages/Authentication.razor) erstellte Seite definiert die Routen, die für die Verarbeitung unterschiedlicher Authentifizierungsstufen erforderlich sind.

Die Komponente RemoteAuthenticatorView:

@page "/authentication/{action}"
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication

<RemoteAuthenticatorView Action="Action" />

@code {
    [Parameter]
    public string? Action { get; set; }
}

Hinweis

Nullwerte zulassende Verweistypen (Nullable Reference Types, NRTs) und die statische Analyse des NULL-Zustands des .NET-Compilers wird in ASP.NET Core in .NET 6 oder höher unterstützt. Vor der Veröffentlichung von ASP.NET Core in .NET 6 wird der string-Typ ohne die NULL-Typbezeichnung (?) angezeigt.

FetchData-Komponente

Dieser Abschnitt bezieht sich auf die Client -App der Lösung.

Die FetchData-Komponente zeigt Folgendes:

  • Stellen Sie ein Zugriffstoken bereit.
  • Verwenden Sie das Zugriffstoken zum Aufrufen einer geschützten Ressourcen-API in der Server-App.

Die @attribute [Authorize]-Anweisung zeigt dem Blazor WebAssembly-Autorisierungssystem an, dass der Benutzer autorisiert werden muss, um diese Komponente anzuzeigen. Das Vorhandensein des Attributs in der Client-App verhindert nicht, dass die API auf dem Server ohne die richtigen Anmeldeinformationen aufgerufen wird. Die Server-App muss auch [Authorize] auf den entsprechenden Endpunkten verwenden, um sie ordnungsgemäß zu schützen.

IAccessTokenProvider.RequestAccessToken übernimmt die Anforderung eines Zugriffstokens, das der Anforderung zum Aufrufen der API hinzugefügt werden kann. Wenn das Token zwischengespeichert wurde oder der Dienst ohne Benutzerinteraktion ein neues Zugriffstoken bereitstellen kann, wird die Tokenanforderung erfolgreich ausgeführt. Andernfalls schlägt die Tokenanforderung mit einer AccessTokenNotAvailableException fehl, die in einer try-catch-Anweisung abgefangen wird.

Um das eigentliche Token abzurufen, das in die Anforderung aufgenommen werden soll, muss die App überprüfen, ob die Anforderung erfolgreich war, indem tokenResult.TryGetToken(out var token) aufgerufen wird.

Wenn die Anforderung erfolgreich ausgeführt wurde, wird die Tokenvariable mit dem Zugriffstoken aufgefüllt. Die AccessToken.Value-Eigenschaft des Tokens macht die Literalzeichenfolge verfügbar, die im Authorization-Anforderungsheader enthalten sein soll.

Wenn bei der Anforderung ein Fehler aufgetreten ist, weil das Token ohne Benutzerinteraktion nicht bereitgestellt werden konnte, geschieht Folgendes:

  • ASP.NET Core in .NET 7 oder höher: Die App navigiert mit den angegebenen AccessTokenResult.InteractionOptions zu AccessTokenResult.InteractiveRequestUrl, um die Aktualisierung des Zugriffstokens zu ermöglichen.
  • ASP.NET Core in .NET 6 oder früher: Das Tokenergebnis enthält eine Umleitungs-URL. Wenn der Benutzer zu dieser URL navigiert, gelangt er zur Anmeldeseite und nach einer erfolgreichen Authentifizierung zurück zur aktuellen Seite.
@page "/fetchdata"
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@using {APP NAMESPACE}.Shared
@attribute [Authorize]
@inject HttpClient Http

...

@code {
    private WeatherForecast[] forecasts;

    protected override async Task OnInitializedAsync()
    {
        try
        {
            forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");
        }
        catch (AccessTokenNotAvailableException exception)
        {
            exception.Redirect();
        }
    }
}

Azure App Service unter Linux

Geben Sie den Zertifikataussteller explizit an, wenn Sie eine Bereitstellung in Azure App Service für Linux durchführen. Weitere Informationen finden Sie unter Verwenden von Identity zum Schützen eines Web-API-Back-Ends für SPAs.

Namens- und Rollenanspruch mit API-Autorisierung

Benutzerdefinierte Benutzerfactory

Erstellen Sie in der Client -App eine benutzerdefinierte Benutzerfactory. IdentityServer sendet mehrere Rollen als JSON-Array in einem einzelnen role-Anspruch. Eine einzelne Rolle wird im Anspruch als Zeichenfolgenwert gesendet. Die Factory erstellt einen einzelnen role-Anspruch für die einzelnen Rollen des Benutzers.

CustomUserFactory.cs:

using System.Security.Claims;
using System.Text.Json;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication.Internal;

public class CustomUserFactory
    : AccountClaimsPrincipalFactory<RemoteUserAccount>
{
    public CustomUserFactory(IAccessTokenProviderAccessor accessor)
        : base(accessor)
    {
    }

    public override async ValueTask<ClaimsPrincipal> CreateUserAsync(
        RemoteUserAccount account,
        RemoteAuthenticationUserOptions options)
    {
        var user = await base.CreateUserAsync(account, options);

        if (user.Identity is not null && user.Identity.IsAuthenticated)
        {
            var identity = (ClaimsIdentity)user.Identity;
            var roleClaims = identity.FindAll(identity.RoleClaimType).ToArray();

            if (roleClaims.Any())
            {
                foreach (var existingClaim in roleClaims)
                {
                    identity.RemoveClaim(existingClaim);
                }

                var rolesElem = 
                    account.AdditionalProperties[identity.RoleClaimType];

                if (options.RoleClaim is not null && rolesElem is JsonElement roles)
                {
                    if (roles.ValueKind == JsonValueKind.Array)
                    {
                        foreach (var role in roles.EnumerateArray())
                        {
                            var roleValue = role.GetString();

                            if (!string.IsNullOrEmpty(roleValue))
                            {
                                identity.AddClaim(
                                  new Claim(options.RoleClaim, roleValue));
                            }

                        }
                    }
                    else
                    {
                        var roleValue = roles.GetString();

                        if (!string.IsNullOrEmpty(roleValue))
                        {
                            identity.AddClaim(
                              new Claim(options.RoleClaim, roleValue));
                        }
                    }
                }
            }
        }

        return user;
    }
}
using System.Linq;
using System.Security.Claims;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication.Internal;

public class CustomUserFactory
    : AccountClaimsPrincipalFactory<RemoteUserAccount>
{
    public CustomUserFactory(IAccessTokenProviderAccessor accessor)
        : base(accessor)
    {
    }

    public override async ValueTask<ClaimsPrincipal> CreateUserAsync(
        RemoteUserAccount account,
        RemoteAuthenticationUserOptions options)
    {
        var user = await base.CreateUserAsync(account, options);

        if (user.Identity.IsAuthenticated)
        {
            var identity = (ClaimsIdentity)user.Identity;
            var roleClaims = identity.FindAll(identity.RoleClaimType).ToArray();

            if (roleClaims.Any())
            {
                foreach (var existingClaim in roleClaims)
                {
                    identity.RemoveClaim(existingClaim);
                }

                var rolesElem = account.AdditionalProperties[identity.RoleClaimType];

                if (rolesElem is JsonElement roles)
                {
                    if (roles.ValueKind == JsonValueKind.Array)
                    {
                        foreach (var role in roles.EnumerateArray())
                        {
                            identity.AddClaim(new Claim(options.RoleClaim, role.GetString()));
                        }
                    }
                    else
                    {
                        identity.AddClaim(new Claim(options.RoleClaim, roles.GetString()));
                    }
                }
            }
        }

        return user;
    }
}

Registrieren Sie in der Client-App die Factory in der Program-Datei:

builder.Services.AddApiAuthorization()
    .AddAccountClaimsPrincipalFactory<CustomUserFactory>();

Rufen Sie in der Server-App AddRoles für den Identity-Generator auf, der rollenbezogene Dienste hinzufügt.

In der Program-Datei:

using Microsoft.AspNetCore.Identity;

...

builder.Services.AddDefaultIdentity<ApplicationUser>(options => 
    options.SignIn.RequireConfirmedAccount = true)
    .AddRoles<IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>();

In Startup.cs:

using Microsoft.AspNetCore.Identity;

...

services.AddDefaultIdentity<ApplicationUser>(options => 
    options.SignIn.RequireConfirmedAccount = true)
    .AddRoles<IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>();

Konfigurieren von IdentityServer

Verwenden Sie einen der folgenden Ansätze:

API-Autorisierungsoptionen

Gehen Sie in der Server -App wie folgt vor:

  • Konfigurieren Sie IdentityServer so, dass der name- und der role-Anspruch in das ID-Token und das Zugriffstoken platziert werden.
  • Verhindern Sie die Standardzuordnung für Rollen im JWT-Tokenhandler.

In der Program-Datei:

using System.IdentityModel.Tokens.Jwt;

...

builder.Services.AddIdentityServer()
    .AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options => {
        options.IdentityResources["openid"].UserClaims.Add("name");
        options.ApiResources.Single().UserClaims.Add("name");
        options.IdentityResources["openid"].UserClaims.Add("role");
        options.ApiResources.Single().UserClaims.Add("role");
    });

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");

In Startup.cs:

using System.IdentityModel.Tokens.Jwt;
using System.Linq;

...

services.AddIdentityServer()
    .AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options => {
        options.IdentityResources["openid"].UserClaims.Add("name");
        options.ApiResources.Single().UserClaims.Add("name");
        options.IdentityResources["openid"].UserClaims.Add("role");
        options.ApiResources.Single().UserClaims.Add("role");
    });

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");

Profildienst

Erstellen Sie in der Server -App eine ProfileService-Implementierung.

ProfileService.cs:

using IdentityModel;
using Duende.IdentityServer.Models;
using Duende.IdentityServer.Services;

public class ProfileService : IProfileService
{
    public ProfileService()
    {
    }

    public async Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        var nameClaim = context.Subject.FindAll(JwtClaimTypes.Name);
        context.IssuedClaims.AddRange(nameClaim);

        var roleClaims = context.Subject.FindAll(JwtClaimTypes.Role);
        context.IssuedClaims.AddRange(roleClaims);

        await Task.CompletedTask;
    }

    public async Task IsActiveAsync(IsActiveContext context)
    {
        await Task.CompletedTask;
    }
}
using IdentityModel;
using Duende.IdentityServer.Models;
using Duende.IdentityServer.Services;
using System.Threading.Tasks;

public class ProfileService : IProfileService
{
    public ProfileService()
    {
    }

    public async Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        var nameClaim = context.Subject.FindAll(JwtClaimTypes.Name);
        context.IssuedClaims.AddRange(nameClaim);

        var roleClaims = context.Subject.FindAll(JwtClaimTypes.Role);
        context.IssuedClaims.AddRange(roleClaims);

        await Task.CompletedTask;
    }

    public async Task IsActiveAsync(IsActiveContext context)
    {
        await Task.CompletedTask;
    }
}

Registrieren Sie in der Server-App den Profildienst in der Program-Datei:

using Duende.IdentityServer.Services;

...

builder.Services.AddTransient<IProfileService, ProfileService>();

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");

Registrieren Sie in der Server-App den Profildienst in Startup.ConfigureServices von Startup.cs:

using IdentityServer4.Services;

...

services.AddTransient<IProfileService, ProfileService>();

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");

Verwenden der Autorisierungsmechanismen

Die Ansätze zur Komponentenautorisierung in der Client -App sind an diesem Punkt funktional. Jeder der Autorisierungsmechanismen bei den Komponenten kann eine Rolle verwenden, um den Benutzer zu autorisieren:

User.Identity.Name wird in der Client -App mit dem Benutzernamen des Benutzers aufgefüllt. In der Regel handelt es sich dabei um die E-Mail-Adresse für die Anmeldung.

UserManager und SignInManager

Legen Sie den Benutzer-ID-Anspruchstyp fest, wenn eine Server-App Folgendes erfordert:

In Program.cs in ASP.NET Core in .NET 6 oder höher:

using System.Security.Claims;

...

builder.Services.Configure<IdentityOptions>(options => 
    options.ClaimsIdentity.UserIdClaimType = ClaimTypes.NameIdentifier);

In Startup.ConfigureServices für ASP.NET Core-Versionen vor Version 6.0:

using System.Security.Claims;

...

services.Configure<IdentityOptions>(options => 
    options.ClaimsIdentity.UserIdClaimType = ClaimTypes.NameIdentifier);

Mit der folgenden WeatherForecastController-Klasse wird UserName protokolliert, wenn die Get-Methode aufgerufen wird.

Hinweis

Im folgenden Beispiel wird ein Dateibereichsnamespace verwendet, bei dem es sich um ein Feature von C# 10 oder höher (.NET 6 oder höher) handelt.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using BlazorSample.Server.Models;
using BlazorSample.Shared;

namespace BlazorSample.Server.Controllers;

[Authorize]
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    private readonly UserManager<ApplicationUser> userManager;

    private static readonly string[] Summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", 
        "Balmy", "Hot", "Sweltering", "Scorching"
    };

    private readonly ILogger<WeatherForecastController> logger;

    public WeatherForecastController(ILogger<WeatherForecastController> logger, 
        UserManager<ApplicationUser> userManager)
    {
        this.logger = logger;
        this.userManager = userManager;
    }

    [HttpGet]
    public async Task<IEnumerable<WeatherForecast>> Get()
    {
        var rng = new Random();

        var user = await userManager.GetUserAsync(User);

        if (user != null)
        {
            logger.LogInformation("User.Identity.Name: {UserIdentityName}", user.UserName);
        }

        return Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = rng.Next(-20, 55),
            Summary = Summaries[rng.Next(Summaries.Length)]
        })
        .ToArray();
    }
}

Im vorherigen Beispiel:

  • Der Server-Namespace des Projekts ist BlazorSample.Server.
  • Der Shared-Namespace des Projekts ist BlazorSample.Shared.

Hosten in Azure App Service mit einer benutzerdefinierten Domäne und einem Zertifikat

In diesem Leitfaden wird Folgendes erläutert:

  • Sie erfahren, wie Sie eine gehostete Blazor WebAssembly-App mit Identity Server für Azure App Service mit einer benutzerdefinierten Domäne bereitstellen.
  • Sie erfahren, wie Sie ein TLS-Zertifikat für Kommunikation mit Browsern über HTTPS erstellen und verwenden. Der Leitfaden konzentriert sich zwar auf die Verwendung des Zertifikats mit einer benutzerdefinierten Domäne, gilt aber ebenso für die Verwendung einer standardmäßigen Azure App-Domäne wie z. B. contoso.azurewebsites.net.

Verwenden Sie für dieses Hostingszenario nicht dasselbe Zertifikat für den Tokensignaturschlüssel von Identity Server und die sichere HTTPS-Kommunikation der Website mit Browsern:

  • Die Verwendung anderer Zertifikate für diese beiden Anforderungen gilt aus Sicherheitsgründen als Best Practice, da private Schlüssel für die einzelnen Zwecke isoliert werden.
  • TLS-Zertifikate für die Kommunikation mit Browsern werden unabhängig verwaltet, ohne dass dadurch eine Auswirkung auf die Tokensignatur von Identity Server entsteht.
  • Wenn Azure Key Vault aus Gründen der Bindung einer benutzerdefinierten Domäne ein Zertifikat für eine App Service-App bereitstellt, kann Identity Server nicht dasselbe Zertifikat von Azure Key Vault abrufen, um das Token zu signieren. Obwohl es möglich ist, dass Identity Server so konfiguriert wird, dass dasselbe TLS-Zertifikat aus einem physischen Pfad verwendet wird, ist das Unterstellen von Zertifikaten unter die Quellcodeverwaltung nicht zu empfehlen und sollte in den meisten Szenarios vermieden werden.

In der folgenden Anleitung wird ein selbstsigniertes Zertifikat in Azure Key Vault erstellt, das lediglich der Tokensignatur in Identity Server dient. Die Identity Server-Konfiguration nutzt das Zertifikat in Key Vault über den CurrentUser>My-Zertifikatspeicher der App. Andere für HTTPS-Datenverkehr mit benutzerdefinierten Domänen verwendete Zertifikate werden unabhängig vom Signaturzertifikat für Identity Server erstellt und konfiguriert.

Konfigurieren einer App sowie Konfigurieren von Azure App Service und Azure Key Vault für Hosting mit benutzerdefinierter Domäne und HTTPS:

  1. Erstellen Sie einen App Service-Plan mindestens mit dem Tarif Basic B1. App Service erfordert mindestens eine Basic B1-Dienstebene, um benutzerdefinierte Domänen nutzen zu können.

  2. Erstellen Sie für eine sichere Browserkommunikation der Website (HTTPS-Protokoll) ein PFX-Zertifikat. Verwenden Sie dabei einen allgemeinen Namen für den vollqualifizierten Domänenname (Fully Qualified Domain Name, FQDN) der Website, den Ihre Organisation steuert, z. B. www.contoso.com. Erstellen Sie das Zertifikat mit folgender Konfiguration:

    • Schlüsselverwendung
      • Digitale Signaturüberprüfung (digitalSignature)
      • Schlüsselverschlüsselung (keyEncipherment)
    • Erweiterte Schlüsselverwendung
      • Clientauthentifizierung (1.3.6.1.5.5.7.3.2)
      • Serverauthentifizierung (1.3.6.1.5.5.7.3.1)

    Verwenden Sie einen der folgenden Ansätze oder ein anderes geeignetes Tool oder einen entsprechenden Onlinedienst, um das Zertifikat zu erstellen:

    Notieren Sie sich das Kennwort, das später verwendet wird, um das Zertifikat in Azure Key Vault zu importieren.

    Weitere Informationen zu Azure Key Vault-Zertifikaten finden Sie unter Azure Key Vault: Zertifikate aus.

  3. Erstellen Sie eine neue Azure Key Vault-Instanz, oder verwenden Sie einen Schlüsseltresor in Ihrem Azure-Abonnement.

  4. Importieren Sie im Bereich Zertifikate des Schlüsseltresors das PFX-Sitezertifikat. Speichern Sie den Zertifikatfingerabdruck, der später für die Konfiguration der App verwendet wird.

  5. Erstellen Sie in Azure Key Vault ein selbstsigniertes Zertifikat für die Identity Server-Tokensignatur. Versehen Sie das Zertifikat mit einem Zertifikatname und einem Antragsteller. Der Antragsteller wird als CN={COMMON NAME} angegeben. Der Platzhalter {COMMON NAME} steht dabei für den allgemeinen Namen des Zertifikats. Beim allgemeinen Namen kann es sich um eine beliebige alphanumerische Zeichenfolge handeln. Bei CN=IdentityServerSigning handelt es sich beispielsweise um einen gültigen Antragsteller für ein Zertifikat. Verwenden Sie unter Ausstellungsrichtlinie>Erweiterte Richtlinienkonfiguration die Standardeinstellungen. Speichern Sie den Zertifikatfingerabdruck, der später für die Konfiguration der App verwendet wird.

  6. Navigieren Sie im Azure-Portal zu Azure App Service, und erstellen Sie eine neu App Service-Instanz mit der folgenden Konfiguration:

    • Legen Sie Veröffentlichen auf Code fest.
    • Legen Sie als Runtimestapel die Runtime der App fest.
    • Bestätigen Sie für SKU und Größe, dass die Dienstebene der App Service-Instanz mindestens Basic B1 ist. App Service erfordert mindestens eine Basic B1-Dienstebene, um benutzerdefinierte Domänen nutzen zu können.
  7. Sobald die App Service-Instanz von Azure erstellt wurde, öffnen Sie die Konfiguration der App, und fügen Sie eine neue Anwendungseinstellung hinzu, die den zuvor gespeicherten Zertifikatfingerabdruck angibt. Der App-Einstellungsschlüssel lautet WEBSITE_LOAD_CERTIFICATES. Trennen Sie die Zertifikatfingerabdrücke im App-Einstellungswert jeweils durch ein Komma, wie es im folgenden Beispiel zu sehen ist:

    • Schlüssel: WEBSITE_LOAD_CERTIFICATES
    • Wert: 57443A552A46DB...D55E28D412B943565,29F43A772CB6AF...1D04F0C67F85FB0B1

    Das Speichern der App-Einstellungen im Azure-Portal ist ein zweistufiger Prozess: Speichern Sie die Schlüssel-Wert-Einstellung WEBSITE_LOAD_CERTIFICATES, und klicken Sie dann oben auf dem Blatt auf die Schaltfläche Speichern.

  8. Klicken Sie auf die TLS-/SSL-Einstellungen der App. Klicken Sie auf Private Schlüsselzertifikate (PFX) . Verwenden Sie den Prozess Key Vault-Zertifikat importieren. Verwenden Sie den Prozess zweimal, um sowohl das Zertifikat der Website für die HTTPS-Kommunikation als auch das selbstsignierte Identity Server-Zertifikat für die Tokensignatur der Website zu importieren.

  9. Navigieren Sie zum Blatt Benutzerdefinierte Domänen. Verwenden Sie auf der Website Ihrer Domänenregistrierungsstelle die IP-Adresse und eine Verifizierungs-ID für eine benutzerdefinierte Domäne, um die Domäne zu konfigurieren. Eine typische Domänenkonfiguration umfasst Folgendes:

    • Einen A-Datensatz mit @ als Host und einen Wert der IP-Adresse aus dem Azure-Portal
    • Einen TXT-Datensatz mit asuid als Host und den Wert der von Azure generierten und vom Azure-Portal bereitgestellten Verifizierungs-ID

    Achten Sie darauf, dass Sie die Änderungen auf der Website Ihrer Domänenregistrierungsstelle korrekt speichern. Für manche Registrierungsstellenwebsites ist ein zweistufiger Prozess erforderlich, um Domänendatensätze zu speichern: Ein oder mehrere Datensätze werden einzeln gespeichert. Dann wird die Registrierung der Domäne über eine eigene Schaltfläche aktualisiert.

  10. Wechseln Sie zurück zum Blatt Benutzerdefinierte Domänen im Azure-Portal. Wählen Sie Benutzerdefinierte Domäne hinzufügen. Klicken Sie auf die Option A-Datensatz. Geben Sie die Domäne an, und klicken Sie auf Überprüfen. Wenn die Domänendatensätze korrekt sind und über das Internet weitergegeben werden, haben Sie im Portal die Möglichkeit, auf die Schaltfläche Benutzerdefinierte Domäne hinzufügen zu klicken.

    Es kann einige Tage dauern, bis Änderungen an der Domänenregistrierung über Domänennamenserver (Domain Name Servers, DNS) im Internet weitergegeben werden, nachdem sie von Ihrer Domänenregistrierungsstelle verarbeitet wurden. Wenn Domänendatensätze nicht innerhalb von drei Werktagen aktualisiert wurden, überprüfen Sie, ob die Datensätze bei der Registrierungsstelle richtig festgelegt wurden, und wenden Sie sich an den entsprechenden Kundensupport.

  11. Auf dem Blatt Benutzerdefinierte Domänen ist die Option SSL-Status für die Domäne mit Not Secure markiert. Klicken Sie auf den Link Bindung hinzufügen. Wählen Sie für die benutzerdefinierte Domänenbindung das HTTPS-Zertifikat der Website aus dem Schlüsseltresor aus.

  12. Öffnen Sie in Visual Studio die Datei mit den App-Einstellungen des Server-Projekts (appsettings.json oder appsettings.Production.json). Fügen Sie in der Identity Server-Konfiguration den folgenden Key-Abschnitt hinzu. Geben Sie den Antragsteller des selbstsignierten Zertifikats für den Name-Schlüssel an. Im folgenden Beispiel handelt es sich beim im Schlüsseltresor zugewiesenen allgemeinen Namen des Zertifikats um IdentityServerSigning, was zu CN=IdentityServerSigning als Antragsteller führt:

    "IdentityServer": {
    
      ...
    
      "Key": {
        "Type": "Store",
        "StoreName": "My",
        "StoreLocation": "CurrentUser",
        "Name": "CN=IdentityServerSigning"
      }
    },
    
  13. Erstellen Sie in Visual Studio ein Azure App Service-Veröffentlichungsprofil für das Server-Projekt. Klicken Sie in der Menüleiste auf Folgendes: Erstellen>Veröffentlichen>Neu>Azure>Azure App Service (Windows oder Linux). Wenn Visual Studio mit einem Azure-Abonnement verbunden ist, können Sie die Anzeige der Azure-Ressourcen nach Ressourcentyp festlegen. Suchen Sie in der Liste mit Web-Apps nach der App Service-Instanz für die App, und klicken Sie darauf. Wählen Sie Fertig stellen aus.

  14. Wenn Sie in Visual Studio zum Veröffentlichen-Fenster zurückgeleitet werden, werden der Schlüsseltresor und die Abhängigkeiten vom SQL Server-Datenbank-Dienst automatisch erkannt.

    Für die Azure Key Vault-Instanz sind keine Konfigurationsänderungen an den Standardeinstellungen erforderlich.

    Zu Testzwecken kann die lokale SQLite-Datenbank einer App, die standardmäßig von der Blazor-Vorlage konfiguriert wird, mit der App ohne zusätzliche Konfiguration bereitgestellt werden. Das Konfigurieren einer anderen Datenbank für Identity Server für Produktionsumgebungen wird in diesem Artikel nicht abgedeckt. Weitere Informationen dazu finden Sie in den Abschnitten zu Datenbankressourcen in den folgenden Dokumentationsartikeln:

  15. Klicken Sie unter dem Namen des Bereitstellungsprofils oben im Fenster auf den Link Bearbeiten. Ändern Sie die Ziel-URL in die URL der benutzerdefinierten Domäne der Website, z. B. https://www.contoso.com. Speichern Sie die Einstellungen.

  16. Veröffentlichen Sie die App. Visual Studio öffnet ein Browserfenster und fordert die Website unter der dazugehörigen benutzerdefinierten Domäne an.

Die Azure-Dokumentation enthält weitere Informationen zur Verwendung von Azure-Diensten und benutzerdefinierten Domänen mit TLS-Bindung in App Service, einschließlich Details zur Verwendung von CNAME-Einträgen anstelle von A-Datensätzen. Weitere Informationen finden Sie in den folgenden Ressourcen:

Wir empfehlen die Verwendung eines neuen Browserfensters für den privaten Modus (z. B. Microsoft Edge InPrivate-Modus oder Google Chrome Inkognito-Modus) für jede Anwendungstestausführung nach einer Änderung an der Anwendung, Anwendungskonfiguration oder Azure-Diensten im Azure-Portal. Veraltete Cookies vorheriger Testläufe können zu Fehlern bei der Authentifizierung oder Autorisierung führen, wenn die Website getestet wird, selbst wenn die Konfiguration der Website korrekt ist. Weitere Informationen darüber, wie Sie Visual Studio so konfigurieren, dass für jeden Testlauf ein neues privates Browserfenster geöffnet wird, finden Sie unter den Abschnitten Cookie und Site-Daten.

Wenn die App Service-Konfiguration im Azure-Portal geändert wird, werden Aktualisierungen in der Regel schnell angewendet, jedoch nicht sofort. Manchmal müssen Sie einen kurzen Zeitraum abwarten, damit App Service neu gestartet wird, damit eine Konfigurationsänderung angewendet werden kann.

Wenn Sie eine Problembehandlung für Ladeprobleme eines Identity Server-Schlüsselsignaturzertifikats ausführen, führen Sie den folgenden Befehl in einer Kudu-PowerShell-Befehlsshell im Azure-Portal aus. Der Befehl stellt eine Zertifikatliste bereit, auf die die App über den CurrentUser>My-Zertifikatspeicher zugreifen kann. Zur Ausgabe gehören auch der Antragsteller von Zertifikaten und Fingerabdrücke, die hilfreich sind, wenn Sie eine App debuggen:

Get-ChildItem -path Cert:\CurrentUser\My -Recurse | Format-List DnsNameList, Subject, Thumbprint, EnhancedKeyUsageList

Problembehandlung

Logging

Informationen zum Aktivieren der Debugging- oder Überwachungsprotokollierung für die Blazor WebAssembly-Authentifizierung finden Sie im Abschnitt Clientseitige Authentifizierungsprotokollierung im Artikel Blazor-Protokollierung in ASP.NET Core unter der Artikelversion ASP.NET Core 7.0 oder höher.

Häufige Fehler

  • Falsche Konfiguration der App oder des Identity-Anbieters (Identity Provider, IP)

    Die häufigsten Fehler werden durch eine falsche Konfiguration verursacht. Im Folgenden finden Sie einige Beispiele:

    • In Abhängigkeit von den Anforderungen des Szenarios verhindert eine fehlende oder falsche Autorität, Instanz, Mandanten-ID, Mandantendomäne oder Client-ID oder ein fehlender oder falscher Umleitungs-URI, dass Clients von einer App authentifiziert werden.
    • Falsche Anforderungsbereiche verhindern, dass Clients auf die Web-API-Endpunkte des Servers zugreifen können.
    • Falsche oder fehlende Server-API-Berechtigungen verhindern, dass Clients auf Server-Web-API-Endpunkte zugreifen können.
    • Das Ausführen der App an einem anderen Port als dem, der im Umleitungs-URI der App-Registrierung für die IP konfiguriert ist. Beachten Sie, dass für Microsoft Entra ID und eine App, die unter einer localhost-Entwicklungstestadresse ausgeführt wird, kein Port erforderlich ist. Die Portkonfiguration der App und der Port, an dem die App ausgeführt wird, müssen bei Nicht-localhost-Adressen jedoch übereinstimmen.

    Die Konfigurationsabschnitte in den Anleitungen in diesem Artikel enthalten Beispiele für die richtige Konfiguration. Lesen Sie jeden Abschnitt des Artikels sorgfältig durch, und achten Sie auf falsche App- und IP-Konfigurationen.

    Wenn die Konfiguration anscheinend korrekt ist:

    • Analysieren Sie Anwendungsprotokolle.

    • Überprüfen Sie den Netzwerkdatenverkehr zwischen der Client-App und dem IP oder der Server-App mit den Entwicklertools des Browsers. Häufig wird vom IP oder von der Server-App eine präzise Fehlermeldung oder eine Meldung mit einem Hinweis auf die Ursache des Problems zurückgegeben, nachdem eine Anforderung erfolgt ist. Anleitungen zu den Entwicklertools finden Sie in den folgenden Artikeln:

    • Decodieren Sie bei Blazor-Releases, in denen ein JSON Web Token (JWT) verwendet wird, den Inhalt des Tokens, das für die Authentifizierung eines Clients oder den Zugriff auf die Web-API des Servers verwendet wird. Letzteres hängt davon ab, wo das Problem auftritt. Weitere Informationen finden Sie unter Überprüfen des Inhalts eines JSON Web Tokens (JWT).

    Das Dokumentationsteam berücksichtigt Feedback zur Dokumentation und zu Fehlern in Artikeln. (Legen Sie im Feedbackbereich auf dieser Seite ein Ticket an.) Es leistet jedoch keinen Produktsupport. Es gibt einige öffentliche Supportforen, die bei der Problembehandlung für eine App weiterhelfen. Es wird Folgendes empfohlen:

    Die genannten Foren werden nicht von Microsoft betrieben oder kontrolliert.

    Bei nicht sicherheitsbezogenen, nicht sensiblen und nicht vertraulichen Fehlerberichten zum Framework wird legen Sie ein Ticket für die ASP.NET Core-Produkteinheit an. Legen Sie ein Ticket für die Produkteinheit erst an, wenn Sie die Ursache eines Problems gründlich untersucht haben und es nicht selbst oder mithilfe der Community in einem öffentlichen Supportforum lösen konnten. Die Produkteinheit kann keine Problembehandlung für einzelne Apps durchführen, die aufgrund einer einfachen Fehlkonfiguration oder in Anwendungsfällen mit Drittanbieterdiensten nicht funktionieren. Wenn ein Bericht sensibler oder vertraulicher Natur ist oder eine potenzielle Sicherheitslücke im Produkt beschreibt, die von Angreifern ausgenutzt werden könnte, lesen Sie bitte den Abschnitt Melden von Sicherheitsproblemen und Fehlern (dotnet/aspnetcoreGitHub Repository).

  • Nicht autorisierter Client für ME-ID

    Info: Die Autorisierung von Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2] Authorization ist fehlgeschlagen. Diese Anforderungen wurden nicht erfüllt: DenyAnonymousAuthorizationRequirement: Erfordert einen authentifizierten Benutzer.

    Anmelderückruffehler von ME-ID:

    • Fehler: unauthorized_client
    • Description (Beschreibung): AADB2C90058: The provided application is not configured to allow public clients.

    So beheben Sie den Fehler

    1. Greifen Sie im Azure-Portal auf das Manifest der App zu.
    2. Legen Sie das allowPublicClient-Attribut auf null oder true fest.

Cookies und Standortdaten

Cookies und Standortdaten können über App-Updates hinweg beibehalten werden und das Testen und die Problembehandlung beeinträchtigen. Entfernen Sie Folgendes, wenn Sie Änderungen am App-Code, Änderungen an den Benutzerkonten beim Anbieter oder Konfigurationsänderungen an Anbieter-Apps vornehmen:

  • cookies für Benutzeranmeldung
  • cookies für App
  • Zwischengespeicherte und gespeicherte Standortdaten

Ein Ansatz, um zu verhindern, dass veraltete cookies und Standortdaten das Testen und die Problembehandlung beeinträchtigen, ist folgender:

  • Browser konfigurieren
    • Verwenden Sie zum Testen einen Browser, den Sie so konfigurieren können, dass alle cookies und Standortdaten jedes Mal gelöscht werden, wenn der Browser geschlossen wird.
    • Stellen Sie sicher, dass der Browser manuell oder durch die IDE für alle Änderungen an der App, dem Testbenutzer oder der Anbieterkonfiguration geschlossen wird.
  • Verwenden Sie einen benutzerdefinierten Befehl, um in Visual Studio einen Browser im privaten oder Inkognito-Modus zu öffnen:
    • Öffnen Sie mithilfe der Schaltfläche Ausführen von Visual Studio das Dialogfeld Browserauswahl.
    • Wählen Sie die Schaltfläche Hinzufügen aus.
    • Geben Sie im Feld Programm den Pfad zu Ihrem Browser an. Die folgenden Pfade für ausführbare Dateien sind typische Installationspfade für Windows 10. Wenn Ihr Browser an einem anderen Speicherort installiert ist oder Sie nicht Windows 10 verwenden, geben Sie den Pfad zur ausführbaren Datei des Browsers an.
      • Microsoft Edge: C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe
      • Google Chrome: C:\Program Files (x86)\Google\Chrome\Application\chrome.exe
      • Mozilla Firefox: C:\Program Files\Mozilla Firefox\firefox.exe
    • Geben Sie im Feld Argumente die Befehlszeilenoption an, die der Browser verwendet, um im privaten oder Inkognito-Modus geöffnet zu werden. Für einige Browser ist die URL der App erforderlich.
      • Microsoft Edge: Verwenden Sie -inprivate.
      • Google Chrome: Verwenden Sie --incognito --new-window {URL}, wobei der Platzhalter {URL} die zu öffnende URL ist (z. B. https://localhost:5001).
      • Mozilla Firefox: Verwenden Sie -private -url {URL}, wobei der Platzhalter {URL} die zu öffnende URL ist (z. B. https://localhost:5001).
    • Geben Sie im Feld Anzeigename einen Namen ein. Beispielsweise Firefox Auth Testing.
    • Klicken Sie auf die Schaltfläche OK.
    • Um zu vermeiden, dass das Browserprofil für jede einzelne Testiteration einer App ausgewählt werden muss, legen Sie das Profil mithilfe der Schaltfläche Als Standard festlegen als Standard fest.
    • Stellen Sie sicher, dass der Browser von der IDE für alle Änderungen an der App, dem Testbenutzer oder der Anbieterkonfiguration geschlossen wird.

App-Upgrades

Eine funktionsfähige App kann direkt nach der Durchführung eines Upgrades für das .NET Core SDK auf dem Entwicklungscomputer oder einer Änderung der Paketversionen in der App fehlschlagen. In einigen Fällen können inkohärente Pakete eine App beschädigen, wenn größere Upgrades durchgeführt werden. Die meisten dieser Probleme können durch Befolgung der folgenden Anweisungen behoben werden:

  1. Löschen Sie die Caches für NuGet-Pakete auf dem lokalen System, indem Sie dotnet nuget locals all --clear in einer Befehlsshell ausführen.
  2. Löschen Sie die Ordner bin und obj des Projekts.
  3. Stellen Sie das Projekt wieder her und erstellen Sie es neu.
  4. Löschen Sie alle Dateien im Bereitstellungsordner auf dem Server, bevor Sie die App noch mal bereitstellen.

Hinweis

Die Verwendung von Paketversionen, die mit dem Zielframework der App nicht kompatibel sind, wird nicht unterstützt. Informationen zu einem Paket finden Sie mithilfe der NuGet Gallery oder des FuGet Package Explorer.

Ausführen der Server-App

Stellen Sie beim Testen und Beheben von Problemen bei einer gehosteten Blazor WebAssembly-Lösung sicher, dass Sie die App aus dem Server-Projekt ausführen.

Überprüfen des Benutzers

Die folgende User-Komponente kann direkt in Anwendungen verwendet werden oder als Grundlage für weitere Anpassungen dienen.

User.razor:

@page "/user"
@attribute [Authorize]
@using System.Text.Json
@using System.Security.Claims
@inject IAccessTokenProvider AuthorizationService

<h1>@AuthenticatedUser?.Identity?.Name</h1>

<h2>Claims</h2>

@foreach (var claim in AuthenticatedUser?.Claims ?? Array.Empty<Claim>())
{
    <p class="claim">@(claim.Type): @claim.Value</p>
}

<h2>Access token</h2>

<p id="access-token">@AccessToken?.Value</p>

<h2>Access token claims</h2>

@foreach (var claim in GetAccessTokenClaims())
{
    <p>@(claim.Key): @claim.Value.ToString()</p>
}

@if (AccessToken != null)
{
    <h2>Access token expires</h2>

    <p>Current time: <span id="current-time">@DateTimeOffset.Now</span></p>
    <p id="access-token-expires">@AccessToken.Expires</p>

    <h2>Access token granted scopes (as reported by the API)</h2>

    @foreach (var scope in AccessToken.GrantedScopes)
    {
        <p>Scope: @scope</p>
    }
}

@code {
    [CascadingParameter]
    private Task<AuthenticationState> AuthenticationState { get; set; }

    public ClaimsPrincipal AuthenticatedUser { get; set; }
    public AccessToken AccessToken { get; set; }

    protected override async Task OnInitializedAsync()
    {
        await base.OnInitializedAsync();
        var state = await AuthenticationState;
        var accessTokenResult = await AuthorizationService.RequestAccessToken();

        if (!accessTokenResult.TryGetToken(out var token))
        {
            throw new InvalidOperationException(
                "Failed to provision the access token.");
        }

        AccessToken = token;

        AuthenticatedUser = state.User;
    }

    protected IDictionary<string, object> GetAccessTokenClaims()
    {
        if (AccessToken == null)
        {
            return new Dictionary<string, object>();
        }

        // header.payload.signature
        var payload = AccessToken.Value.Split(".")[1];
        var base64Payload = payload.Replace('-', '+').Replace('_', '/')
            .PadRight(payload.Length + (4 - payload.Length % 4) % 4, '=');

        return JsonSerializer.Deserialize<IDictionary<string, object>>(
            Convert.FromBase64String(base64Payload));
    }
}

Überprüfen des Inhalts eines JSON Web Tokens (JWT)

Verwenden Sie zum Decodieren eines JSON Web Tokens (JWT) das Tool jwt.ms von Microsoft. Werte in der Benutzeroberfläche verlassen nie Ihren Browser.

Beispiel für ein codiertes JWT (für die Darstellung gekürzt):

eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ilg1ZVhrNHh5b2pORnVtMWtsMll0djhkbE5QNC1j ... bQdHBHGcQQRbW7Wmo6SWYG4V_bU55Ug_PW4pLPr20tTS8Ct7_uwy9DWrzCMzpD-EiwT5IjXwlGX3IXVjHIlX50IVIydBoPQtadvT7saKo1G5Jmutgq41o-dmz6-yBMKV2_nXA25Q

Beispiel-JWT, das mit dem Tool für eine App decodiert wurde, das bei Azure AAD B2C authentifiziert wird:

{
  "typ": "JWT",
  "alg": "RS256",
  "kid": "X5eXk4xyojNFum1kl2Ytv8dlNP4-c57dO6QGTVBwaNk"
}.{
  "exp": 1610059429,
  "nbf": 1610055829,
  "ver": "1.0",
  "iss": "https://mysiteb2c.b2clogin.com/5cc15ea8-a296-4aa3-97e4-226dcc9ad298/v2.0/",
  "sub": "5ee963fb-24d6-4d72-a1b6-889c6e2c7438",
  "aud": "70bde375-fce3-4b82-984a-b247d823a03f",
  "nonce": "b2641f54-8dc4-42ca-97ea-7f12ff4af871",
  "iat": 1610055829,
  "auth_time": 1610055822,
  "idp": "idp.com",
  "tfp": "B2C_1_signupsignin"
}.[Signature]

Zusätzliche Ressourcen