Zabezpieczanie hostowanej aplikacji ASP.NET Core Blazor WebAssembly za pomocą Identity serwera

W tym artykule wyjaśniono, jak utworzyć hostowane Blazor WebAssembly rozwiązanie, które używa serwera Duende Identity do uwierzytelniania użytkowników i wywołań interfejsu API.

Ważne

Oprogramowanie Duende może wymagać zapłacenia opłaty licencyjnej za korzystanie z serwera Duende Identity Server w środowisku produkcyjnym. Aby uzyskać więcej informacji, zobacz Migracja z platformy ASP.NET Core w wersji 5.0 do wersji 6.0.

Uwaga

Aby skonfigurować autonomiczną lub hostowaną Blazor WebAssembly aplikację do korzystania z istniejącego wystąpienia serwera zewnętrznego Identity , postępuj zgodnie ze wskazówkami w temacie Zabezpieczanie autonomicznej aplikacji ASP.NET Core Blazor WebAssembly za pomocą biblioteki uwierzytelniania.

Aby uzyskać dodatkowe pokrycie scenariuszy zabezpieczeń po przeczytaniu tego artykułu, zobacz ASP.NET Core Blazor WebAssembly dodatkowe scenariusze zabezpieczeń.

Przewodnik

Podsekcje przewodnika wyjaśniają, jak:

  • Blazor Tworzenie aplikacji
  • Uruchom aplikację

Tworzenie Blazor aplikacji

Aby utworzyć nowy Blazor WebAssembly projekt z mechanizmem uwierzytelniania:

  1. Tworzenie nowego projektu.

  2. Blazor WebAssembly Wybierz szablon Aplikacja. Wybierz Dalej.

  3. Podaj nazwę projektu bez użycia kreski. Upewnij się, że lokalizacja jest poprawna. Wybierz Dalej.

    Unikaj używania łączników (-) w nazwie projektu, które przerywają tworzenie identyfikatora aplikacji OIDC. Logika w szablonie Blazor WebAssembly projektu używa nazwy projektu dla identyfikatora aplikacji OIDC w konfiguracji rozwiązania, a łączniki nie są dozwolone w identyfikatorze aplikacji OIDC. Przypadek Pascal () lub podkreślenia (BlazorSampleBlazor_Sample) są akceptowalnymi alternatywami.

  4. W oknie dialogowym Dodatkowe informacje wybierz pozycję Indywidualne konta jako typ uwierzytelniania, aby przechowywać użytkowników w aplikacji przy użyciu systemu ASP.NET CoreIdentity.

  5. Zaznacz pole wyboru ASP.NET Core Hosted.

  6. Wybierz przycisk Utwórz, aby utworzyć aplikację.

Uruchom aplikację

Uruchom aplikację z Server projektu. W przypadku korzystania z programu Visual Studio:

  • Wybierz strzałkę listy rozwijanej obok przycisku Uruchom . Otwórz pozycję Konfiguruj projekty startowe z listy rozwijanej. Wybierz opcję Pojedynczy projekt startowy. Potwierdź lub zmień projekt projektu startowego na Server projekt.

  • Upewnij się, że Server projekt został wyróżniony w Eksplorator rozwiązań przed rozpoczęciem aplikacji przy użyciu dowolnego z następujących podejść:

    • Wybierz przycisk Run (Uruchom).
    • Użyj polecenia Debuguj>Rozpocznij debugowanie z menu.
    • Naciśnij klawisz F5.
  • W powłoce poleceń przejdź do Server folderu projektu rozwiązania. Wykonaj polecenie dotnet run.

Części rozwiązania

W tej sekcji opisano części rozwiązania wygenerowane na Blazor WebAssembly podstawie szablonu projektu i opisano sposób konfigurowania Client rozwiązań i Server projektów do celów referencyjnych. Nie ma konkretnych wskazówek, które należy wykonać w tej sekcji dla podstawowej aplikacji roboczej, jeśli aplikacja została utworzona przy użyciu wskazówek w sekcji Przewodnik . Wskazówki zawarte w tej sekcji są przydatne podczas aktualizowania aplikacji w celu uwierzytelniania i autoryzowania użytkowników. Jednak alternatywnym podejściem do aktualizowania aplikacji jest utworzenie nowej aplikacji na podstawie wskazówek w sekcji Przewodnik i przeniesienie składników, klas i zasobów aplikacji do nowej aplikacji.

Server app services

Ta sekcja dotyczy aplikacji rozwiązania Server .

Zarejestrowane są następujące usługi.

  • W pliku Program:

    • Entity Framework Core i 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 Serwer z dodatkową AddApiAuthorization metodą pomocnika, która konfiguruje domyślne konwencje ASP.NET Core na serwerze Identity :

      builder.Services.AddIdentityServer()
          .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
      
    • Uwierzytelnianie za pomocą dodatkowej AddIdentityServerJwt metody pomocniczej, która konfiguruje aplikację do weryfikowania tokenów JWT utworzonych przez Identity serwer:

      builder.Services.AddAuthentication()
          .AddIdentityServerJwt();
      
  • W Startup.ConfigureServices pliku :Startup.cs

    • Entity Framework Core i ASP.NET Core Identity:

      services.AddDbContext<ApplicationDbContext>(options =>
          options.UseSqlite(
              Configuration.GetConnectionString("DefaultConnection")));
      
      services.AddDefaultIdentity<ApplicationUser>(options => 
              options.SignIn.RequireConfirmedAccount = true)
          .AddEntityFrameworkStores<ApplicationDbContext>();
      
    • Identity Serwer z dodatkową AddApiAuthorization metodą pomocnika, która konfiguruje domyślne konwencje ASP.NET Core na serwerze Identity :

      services.AddIdentityServer()
          .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
      
    • Uwierzytelnianie za pomocą dodatkowej AddIdentityServerJwt metody pomocniczej, która konfiguruje aplikację do weryfikowania tokenów JWT utworzonych przez Identity serwer:

      services.AddAuthentication()
          .AddIdentityServerJwt();
      

Uwaga

Po zarejestrowaniu pojedynczego schematu uwierzytelniania schemat uwierzytelniania jest automatycznie używany jako schemat domyślny aplikacji i nie jest konieczne podanie schematu do AddAuthentication programu lub za pośrednictwem metody AuthenticationOptions. Aby uzyskać więcej informacji, zobacz Overview of ASP.NET Core Authentication and the ASP.NET Core anons (aspnet/Announcements #490).

  • W pliku Program:
  • W Startup.Configure pliku :Startup.cs
  • Identity Oprogramowanie pośredniczące serwera uwidacznia punkty końcowe openID Połączenie (OIDC):

    app.UseIdentityServer();
    
  • Oprogramowanie pośredniczące uwierzytelniania jest odpowiedzialne za weryfikowanie poświadczeń żądania i ustawianie użytkownika w kontekście żądania:

    app.UseAuthentication();
    
  • Oprogramowanie pośredniczące autoryzacji umożliwia autoryzację:

    app.UseAuthorization();
    

Autoryzacja interfejsu API

Ta sekcja dotyczy aplikacji rozwiązania Server .

Metoda AddApiAuthorization pomocnika konfiguruje Identity serwer dla scenariuszy ASP.NET Core. Identity Serwer to zaawansowana i rozszerzalna struktura do obsługi problemów z zabezpieczeniami aplikacji. Identity Serwer uwidacznia niepotrzebną złożoność w przypadku najbardziej typowych scenariuszy. W związku z tym zapewnia się zestaw konwencji i opcji konfiguracji, które uważamy za dobry punkt wyjścia. Po zmianie potrzeb związanych z uwierzytelnianiem pełna moc serwera Identity jest dostępna w celu dostosowania uwierzytelniania zgodnie z wymaganiami aplikacji.

Dodawanie procedury obsługi uwierzytelniania dla interfejsu API, który współistnieje z serwerem Identity

Ta sekcja dotyczy aplikacji rozwiązania Server .

Metoda AddIdentityServerJwt pomocnika konfiguruje schemat zasad dla aplikacji jako domyślną procedurę obsługi uwierzytelniania. Zasady są skonfigurowane tak, aby umożliwić Identity obsługę wszystkich żądań kierowanych do dowolnej ścieżki podrzędnej w obszarze adresu URL w obszarze Identity/Identity. Usługa JwtBearerHandler obsługuje wszystkie inne żądania. Ponadto ta metoda:

  • Rejestruje zasób interfejsu API za pomocą Identity serwera z domyślnym zakresem {PROJECT NAME}API, gdzie {PROJECT NAME} symbol zastępczy jest nazwą projektu podczas tworzenia aplikacji.
  • Konfiguruje oprogramowanie pośredniczące tokenu elementu nośnego JWT w celu sprawdzania poprawności tokenów wystawionych przez Identity serwer dla aplikacji.

Kontroler prognozy pogody

Ta sekcja dotyczy aplikacji rozwiązania Server .

W elemecie WeatherForecastController (Controllers/WeatherForecastController.cs) [Authorize] atrybut jest stosowany do klasy. Atrybut wskazuje, że użytkownik musi być autoryzowany na podstawie domyślnych zasad dostępu do zasobu. Domyślne zasady autoryzacji są skonfigurowane do używania domyślnego schematu uwierzytelniania skonfigurowanego przez AddIdentityServerJwtprogram . Metoda pomocnika jest JwtBearerHandler konfigurowana jako domyślna procedura obsługi żądań do aplikacji.

Kontekst bazy danych aplikacji

Ta sekcja dotyczy aplikacji rozwiązania Server .

W elemecie () jest rozszerzany ApiAuthorizationDbContext<TUser> o uwzględnienie schematu serweraIdentity. DbContextData/ApplicationDbContext.csApplicationDbContext ApiAuthorizationDbContext<TUser> pochodzi z IdentityDbContext.

Aby uzyskać pełną kontrolę nad schematem bazy danych, dziedziczyć z jednej z dostępnych IdentityDbContext klas i skonfigurować kontekst, aby uwzględnić Identity schemat przez wywołanie builder.ConfigurePersistedGrantContext(_operationalStoreOptions.Value) metody OnModelCreating .

Kontroler konfiguracji OIDC

Ta sekcja dotyczy aplikacji rozwiązania Server .

W elemecie OidcConfigurationController (Controllers/OidcConfigurationController.cs) punkt końcowy klienta jest aprowizowany w celu obsługi parametrów OIDC.

Ustawienia aplikacji

Ta sekcja dotyczy aplikacji rozwiązania Server .

W pliku ustawień aplikacji (appsettings.json) w katalogu głównym IdentityServer projektu sekcja opisuje listę skonfigurowanych klientów. W poniższym przykładzie istnieje jeden klient. Nazwa klienta odpowiada Client nazwie zestawu aplikacji i jest mapowana zgodnie z konwencją do parametru OAuth ClientId . Profil wskazuje skonfigurowany typ aplikacji. Profil jest używany wewnętrznie do określania konwencji, które upraszczają proces konfiguracji serwera.

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

Symbol zastępczy {ASSEMBLY NAME} to Client nazwa zestawu aplikacji (na przykład BlazorSample.Client).

Pakiet uwierzytelniania

Ta sekcja dotyczy aplikacji rozwiązania Client .

Po utworzeniu aplikacji do korzystania z indywidualnych kont użytkowników (Individual) aplikacja automatycznie otrzymuje odwołanie do Microsoft.AspNetCore.Components.WebAssembly.Authentication pakietu. Pakiet zawiera zestaw elementów pierwotnych, które ułatwiają aplikacji uwierzytelnianie użytkowników i uzyskiwanie tokenów w celu wywoływania chronionych interfejsów API.

W przypadku dodawania uwierzytelniania do aplikacji ręcznie dodaj Microsoft.AspNetCore.Components.WebAssembly.Authentication pakiet do aplikacji.

Uwaga

Aby uzyskać instrukcje dodawania pakietów do aplikacji .NET, zobacz artykuły w sekcji Instalowanie pakietów i zarządzanie nimi w temacie Przepływ pracy użycia pakietów (dokumentacja programu NuGet). Sprawdź prawidłowe wersje pakietów pod adresem NuGet.org.

Konfiguracja widoku HttpClient

Ta sekcja dotyczy aplikacji rozwiązania Client .

Program W pliku nazwana HttpClient jest skonfigurowana do dostarczania HttpClient wystąpień zawierających tokeny dostępu podczas podejmowania żądań do interfejsu API serwera. Domyślnie podczas tworzenia rozwiązania nazwa to HttpClient{PROJECT NAME}.ServerAPI, gdzie {PROJECT NAME} symbol zastępczy to nazwa projektu.

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

Symbol zastępczy {PROJECT NAME} to nazwa projektu podczas tworzenia rozwiązania. Na przykład podanie nazwy BlazorSample projektu powoduje utworzenie nazwy HttpClientBlazorSample.ServerAPI.

Uwaga

Jeśli konfigurujesz aplikację Blazor WebAssembly tak, aby korzystała z istniejącego Identity wystąpienia serwera, które nie jest częścią rozwiązania hostowanego Blazor , zmień HttpClient rejestrację adresu podstawowego z IWebAssemblyHostEnvironment.BaseAddress (builder.HostEnvironment.BaseAddress) na adres URL punktu końcowego autoryzacji interfejsu API serwera.

Obsługa autoryzacji interfejsu API

Ta sekcja dotyczy aplikacji rozwiązania Client .

Obsługa uwierzytelniania użytkowników jest podłączona do kontenera usługi przez metodę rozszerzenia podaną Microsoft.AspNetCore.Components.WebAssembly.Authentication wewnątrz pakietu. Ta metoda konfiguruje usługi wymagane przez aplikację do interakcji z istniejącym systemem autoryzacji.

builder.Services.AddApiAuthorization();

Domyślnie konfiguracja aplikacji jest ładowana zgodnie z konwencją z ._configuration/{client-id} Zgodnie z konwencją identyfikator klienta jest ustawiony na nazwę zestawu aplikacji. Ten adres URL można zmienić tak, aby wskazywał oddzielny punkt końcowy, wywołując przeciążenie za pomocą opcji.

Plik Imports

Ta sekcja dotyczy aplikacji rozwiązania Client .

Microsoft.AspNetCore.Components.Authorization Przestrzeń nazw jest udostępniana w całej _Imports.razor aplikacji za pośrednictwem pliku:

@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 Strona

Ta sekcja dotyczy aplikacji rozwiązania Client .

Strona Indeks (wwwroot/index.html) zawiera skrypt, który definiuje element AuthenticationService w języku JavaScript. AuthenticationService obsługuje szczegóły niskiego poziomu protokołu OIDC. Aplikacja wewnętrznie wywołuje metody zdefiniowane w skry skryptie w celu wykonania operacji uwierzytelniania.

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

App cm6long

Ta sekcja dotyczy aplikacji rozwiązania Client .

Składnik App (App.razor) jest podobny do składnika znajdującego App się w Blazor Server aplikacjach:

  • Składnik CascadingAuthenticationState zarządza udostępnianiem AuthenticationState pozostałej części aplikacji.
  • Składnik AuthorizeRouteView zapewnia, że bieżący użytkownik ma autoryzację dostępu do danej strony lub w inny sposób renderuje RedirectToLogin składnik.
  • Składnik RedirectToLogin zarządza przekierowywaniem nieautoryzowanych użytkowników do strony logowania.

Ze względu na zmiany w strukturze w wersjach ASP.NET Core Razor , znaczniki dla App składnika (App.razor) nie są wyświetlane w tej sekcji. Aby sprawdzić znaczniki składnika dla danej wersji, użyj jednej z następujących metod:

  • Utwórz aplikację aprowizowaną do uwierzytelniania na podstawie domyślnego Blazor WebAssembly szablonu projektu dla wersji ASP.NET Core, która ma być używana. App Sprawdź składnik (App.razor) w wygenerowanej aplikacji.

  • App Sprawdź składnik (App.razor) w źródle referencyjnym. Wybierz wersję z selektora gałęzi i wyszukaj składnik w ProjectTemplates folderze repozytorium, ponieważ został przeniesiony przez lata.

    Uwaga

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

RedirectToLogin cm6long

Ta sekcja dotyczy aplikacji rozwiązania Client .

Składnik RedirectToLogin (RedirectToLogin.razor):

  • Zarządza przekierowywaniem nieautoryzowanych użytkowników do strony logowania.
  • Bieżący adres URL, do którego użytkownik próbuje uzyskać dostęp, jest utrzymywany przez program , dzięki czemu może zostać zwrócony do tej strony, jeśli uwierzytelnianie zakończy się pomyślnie, użyj:
    • Stan historii nawigacji w programie ASP.NET Core na platformie .NET 7 lub nowszym.
    • Ciąg zapytania w programie ASP.NET Core na platformie .NET 6 lub starszej wersji.

RedirectToLogin Sprawdź składnik w źródle referencyjnym. Lokalizacja składnika zmieniła się wraz z upływem czasu, dlatego użyj narzędzi wyszukiwania GitHub, aby zlokalizować składnik.

Uwaga

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

LoginDisplay cm6long

Ta sekcja dotyczy aplikacji rozwiązania Client .

Składnik LoginDisplay () jest renderowany w składniku MainLayout (LoginDisplay.razorMainLayout.razor) i zarządza następującymi zachowaniami:

  • W przypadku uwierzytelnionych użytkowników:
    • Wyświetla bieżącą nazwę użytkownika.
    • Oferuje link do strony profilu użytkownika w programie ASP.NET Core Identity.
    • Oferuje przycisk wylogowywuje się z aplikacji.
  • W przypadku użytkowników anonimowych:
    • Oferuje opcję rejestracji.
    • Oferuje opcję logowania.

Ze względu na zmiany w strukturze w wersjach ASP.NET Core Razor , znaczniki dla LoginDisplay składnika nie są wyświetlane w tej sekcji. Aby sprawdzić znaczniki składnika dla danej wersji, użyj jednej z następujących metod:

  • Utwórz aplikację aprowizowaną do uwierzytelniania na podstawie domyślnego Blazor WebAssembly szablonu projektu dla wersji ASP.NET Core, która ma być używana. LoginDisplay Sprawdź składnik w wygenerowanej aplikacji.

  • LoginDisplay Sprawdź składnik w źródle referencyjnym. Lokalizacja składnika zmieniła się wraz z upływem czasu, dlatego użyj narzędzi wyszukiwania GitHub, aby zlokalizować składnik. Użyto szablonowej zawartości równej Hostedtrue .

    Uwaga

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

Authentication cm6long

Ta sekcja dotyczy aplikacji rozwiązania Client .

Strona utworzona Authentication przez składnik (Pages/Authentication.razor) definiuje trasy wymagane do obsługi różnych etapów uwierzytelniania.

Składnik RemoteAuthenticatorView :

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

<RemoteAuthenticatorView Action="Action" />

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

Uwaga

Statyczne analizy statyczne typu referencyjnego dopuszczania wartości null (NRT) i kompilatora platformy .NET są obsługiwane w programie ASP.NET Core na platformie .NET 6 lub nowszym. Przed wydaniem ASP.NET Core na platformie .NET 6 string typ jest wyświetlany bez oznaczenia typu null (?).

FetchData cm6long

Ta sekcja dotyczy aplikacji rozwiązania Client .

Składnik FetchData pokazuje, jak:

  • Aprowizuj token dostępu.
  • Użyj tokenu dostępu, aby wywołać chroniony interfejs API zasobów w aplikacji Serwera .

Dyrektywa @attribute [Authorize] wskazuje Blazor WebAssembly system autoryzacji, że użytkownik musi być autoryzowany w celu odwiedzenia tego składnika. Obecność atrybutu w Client aplikacji nie uniemożliwia wywoływanie interfejsu API na serwerze bez odpowiednich poświadczeń. Aplikacja Server musi również używać [Authorize] w odpowiednich punktach końcowych, aby je prawidłowo chronić.

IAccessTokenProvider.RequestAccessToken Zajmuje się żądaniem tokenu dostępu, który można dodać do żądania w celu wywołania interfejsu API. Jeśli token jest buforowany lub usługa może aprowizować nowy token dostępu bez interakcji użytkownika, żądanie tokenu zakończy się pomyślnie. W przeciwnym razie żądanie tokenu kończy się niepowodzeniem z elementem AccessTokenNotAvailableException, który jest przechwycony w instrukcji try-catch .

Aby uzyskać rzeczywisty token do uwzględnienia w żądaniu, aplikacja musi sprawdzić, czy żądanie zakończyło się pomyślnie, wywołując metodę tokenResult.TryGetToken(out var token).

Jeśli żądanie zakończyło się pomyślnie, zmienna tokenu zostanie wypełniona tokenem dostępu. AccessToken.Value Właściwość tokenu uwidacznia ciąg literału do uwzględnienia w nagłówku Authorization żądania.

Jeśli żądanie nie powiodło się, ponieważ nie można aprowizować tokenu bez interakcji użytkownika:

  • ASP.NET Core na platformie .NET 7 lub nowszym: aplikacja przechodzi do metody przy użyciu podanejAccessTokenResult.InteractionOptions, aby zezwolić AccessTokenResult.InteractiveRequestUrl na odświeżanie tokenu dostępu.
  • ASP.NET Core na platformie .NET 6 lub starszej wersji: wynik tokenu zawiera adres URL przekierowania. Przejście do tego adresu URL powoduje przejście użytkownika do strony logowania i powrót do bieżącej strony po pomyślnym uwierzytelnieniu.
@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();
        }
    }
}

usługa aplikacja systemu Azure w systemie Linux

Określ wystawcę jawnie podczas wdrażania w usłudze aplikacja systemu Azure w systemie Linux. Aby uzyskać więcej informacji, zobacz Zabezpieczanie Identity zaplecza internetowego interfejsu API dla spAs.

Nazwa i oświadczenie roli z autoryzacją interfejsu API

Niestandardowa fabryka użytkowników

Client W aplikacji utwórz niestandardową fabrykę użytkowników. Identity Serwer wysyła wiele ról jako tablicę JSON w jednym role oświadczeniu. Pojedyncza rola jest wysyłana jako wartość ciągu w oświadczeniu. Fabryka tworzy indywidualne role oświadczenie dla każdej roli użytkownika.

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

Client W aplikacji zarejestruj fabrykę w Program pliku:

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

W aplikacji wywołaj ServerAddRolesIdentity konstruktora, który dodaje usługi związane z rolami.

W pliku Program:

using Microsoft.AspNetCore.Identity;

...

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

W pliku Startup.cs:

using Microsoft.AspNetCore.Identity;

...

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

Konfigurowanie Identity serwera

Użyj jednego z następujących podejść:

Opcje autoryzacji interfejsu API

Server W aplikacji:

  • Skonfiguruj Identity serwer, aby umieścić name oświadczenia i role w tokenie identyfikatora i tokenu dostępu.
  • Zapobiegaj domyślnemu mapowaniu ról w procedurze obsługi tokenów JWT.

W pliku Program:

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

W pliku 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");

Usługa profilu

Server W aplikacji utwórz implementacjęProfileService.

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

Server W aplikacji zarejestruj usługę profilu w Program pliku :

using Duende.IdentityServer.Services;

...

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

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");

Server W aplikacji zarejestruj usługę profilu w Startup.ConfigureServices pliku Startup.cs:

using IdentityServer4.Services;

...

services.AddTransient<IProfileService, ProfileService>();

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");

Korzystanie z mechanizmów autoryzacji

Client W aplikacji podejścia autoryzacji składników działają w tym momencie. Dowolny z mechanizmów autoryzacji w składnikach może używać roli do autoryzowania użytkownika:

User.Identity.Name jest wypełniany w Client aplikacji nazwą użytkownika, czyli zazwyczaj adresem e-mail logowania.

UserManager i SignInManager

Ustaw typ oświadczenia identyfikatora użytkownika, gdy aplikacja serwera wymaga:

W Program.cs programie for ASP.NET Core na platformie .NET 6 lub nowszym:

using System.Security.Claims;

...

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

W Startup.ConfigureServices przypadku wersji ASP.NET Core starszych niż 6.0:

using System.Security.Claims;

...

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

Poniżej WeatherForecastController przedstawiono dzienniki UserName po wywołaniu Get metody .

Uwaga

W poniższym przykładzie użyto przestrzeni nazw o zakresie plików, która jest funkcją języka C# 10 lub nowszego (.NET 6 lub nowsza).

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

W powyższym przykładzie:

  • Server Przestrzeń nazw projektu to BlazorSample.Server.
  • Shared Przestrzeń nazw projektu to BlazorSample.Shared.

Host w usłudze aplikacja systemu Azure z domeną niestandardową i certyfikatem

W poniższych wskazówkach wyjaśniono:

  • Jak wdrożyć hostowaną Blazor WebAssembly aplikację z serwerem Identity w celu aplikacja systemu Azure Service z domeną niestandardową.
  • Jak utworzyć i używać certyfikatu TLS do komunikacji protokołu HTTPS z przeglądarkami. Chociaż wskazówki koncentrują się na korzystaniu z certyfikatu z domeną niestandardową, wskazówki mają równie zastosowanie do używania domyślnej domeny aplikacja systemu Azure s, na przykład contoso.azurewebsites.net.

W tym scenariuszu hostingu nie należy używać tego samego certyfikatu dla Identity klucza podpisywania tokenu serwera i bezpiecznej komunikacji HTTPS witryny z przeglądarkami:

  • Używanie różnych certyfikatów dla tych dwóch wymagań jest dobrym rozwiązaniem w zakresie zabezpieczeń, ponieważ izoluje klucze prywatne do każdego celu.
  • Certyfikaty TLS do komunikacji z przeglądarkami są zarządzane niezależnie bez wpływu na Identity podpisywanie tokenu serwera.
  • Gdy usługa Azure Key Vault dostarcza certyfikat do aplikacji usługi App Service na potrzeby powiązania domeny niestandardowej, Identity serwer nie może uzyskać tego samego certyfikatu z usługi Azure Key Vault na potrzeby podpisywania tokenów. Chociaż skonfigurowanie Identity serwera do używania tego samego certyfikatu TLS ze ścieżki fizycznej jest możliwe, umieszczenie certyfikatów zabezpieczeń w kontroli źródła jest złym rozwiązaniem i należy unikać go w większości scenariuszy.

W poniższych wskazówkach w usłudze Azure Key Vault jest tworzony certyfikat z podpisem własnym wyłącznie na potrzeby Identity podpisywania tokenów serwera. Konfiguracja Identity serwera używa certyfikatu magazynu kluczy za pośrednictwem magazynu certyfikatów aplikacji CurrentUser>My . Inne certyfikaty używane do ruchu HTTPS z domenami niestandardowymi są tworzone i konfigurowane oddzielnie od certyfikatu podpisywania Identity serwera.

Aby skonfigurować aplikację, usługę aplikacja systemu Azure i usługę Azure Key Vault do hostowania przy użyciu domeny niestandardowej i protokołu HTTPS:

  1. Utwórz plan usługi App Service z poziomem Basic B1 planu lub wyższym. Usługa App Service wymaga warstwy Basic B1 usługi lub wyższej do korzystania z domen niestandardowych.

  2. Utwórz certyfikat PFX dla bezpiecznej komunikacji przeglądarki (protokołu HTTPS) witryny o nazwie pospolitej w pełni kwalifikowanej nazwy domeny (FQDN), którą kontroluje Organizacja (na przykład www.contoso.com). Utwórz certyfikat za pomocą:

    • Użycie klucza
      • Walidacja podpisu cyfrowego (digitalSignature)
      • Szyfrowanie klucza (keyEncipherment)
    • Rozszerzone/rozszerzone zastosowania kluczy
      • Uwierzytelnianie klienta (1.3.6.1.5.5.7.3.2)
      • Uwierzytelnianie serwera (1.3.6.1.5.5.7.3.1)

    Aby utworzyć certyfikat, użyj jednego z następujących metod lub innego odpowiedniego narzędzia lub usługi online:

    Zanotuj hasło, które jest później używane do importowania certyfikatu do usługi Azure Key Vault.

    Aby uzyskać więcej informacji na temat certyfikatów usługi Azure Key Vault, zobacz Usługa Azure Key Vault: certyfikaty.

  3. Utwórz nową usługę Azure Key Vault lub użyj istniejącego magazynu kluczy w ramach subskrypcji platformy Azure.

  4. W obszarze Certyfikaty magazynu kluczy zaimportuj certyfikat lokacji PFX. Zarejestruj odcisk palca certyfikatu, który jest używany w konfiguracji aplikacji później.

  5. W usłudze Azure Key Vault wygeneruj nowy certyfikat z podpisem własnym na potrzeby Identity podpisywania tokenu serwera. Nadaj certyfikatowi nazwę certyfikatu i podmiot. Podmiot jest określony jako CN={COMMON NAME}, gdzie {COMMON NAME} symbol zastępczy jest nazwą pospolitą certyfikatu. Nazwa pospolita może być dowolnym ciągiem alfanumerycznym. Na przykład CN=IdentityServerSigning jest prawidłowym podmiotem certyfikatu. W obszarze Konfiguracja zaawansowanych zasad> wystawiania użyj ustawień domyślnych. Zarejestruj odcisk palca certyfikatu, który jest używany w konfiguracji aplikacji później.

  6. Przejdź do usługi aplikacja systemu Azure w witrynie Azure Portal i utwórz nową usługę App Service z następującą konfiguracją:

    • Opublikuj w ustawieniu na Code.
    • Stos środowiska uruchomieniowego ustawiony na środowisko uruchomieniowe aplikacji.
    • W przypadku jednostki SKU i rozmiaru upewnij się, że warstwa usługi App Service jest Basic B1 lub wyższa. Usługa App Service wymaga warstwy Basic B1 usługi lub wyższej do korzystania z domen niestandardowych.
  7. Po utworzeniu usługi App Service przez platformę Azure otwórz konfigurację aplikacji i dodaj nowe ustawienie aplikacji określające zarejestrowane wcześniej odciski palca certyfikatu. Klucz ustawień aplikacji to WEBSITE_LOAD_CERTIFICATES. Rozdziel odciski palca certyfikatu w wartości ustawienia aplikacji przecinkami, jak pokazano w poniższym przykładzie:

    • Klucz: WEBSITE_LOAD_CERTIFICATES
    • Wartość: 57443A552A46DB...D55E28D412B943565,29F43A772CB6AF...1D04F0C67F85FB0B1

    W witrynie Azure Portal zapisywanie ustawień aplikacji jest procesem dwuetapowym: Zapisz WEBSITE_LOAD_CERTIFICATES ustawienie klucz-wartość, a następnie wybierz przycisk Zapisz w górnej części bloku.

  8. Wybierz ustawienia protokołu TLS/SSL aplikacji. Wybierz pozycję Certyfikaty klucza prywatnego (pfx). Użyj procesu Importowanie certyfikatu usługi Key Vault. Użyj procesu dwa razy , aby zaimportować zarówno certyfikat lokacji na potrzeby komunikacji HTTPS, jak i certyfikat podpisywania tokenu serwera z podpisem Identity własnym witryny.

  9. Przejdź do bloku Domeny niestandardowe. W witrynie internetowej rejestratora domen użyj adresu IP i niestandardowego identyfikatora weryfikacji domeny, aby skonfigurować domenę. Typowa konfiguracja domeny obejmuje:

    • Rekord A z wartością Host@ i wartość adresu IP z witryny Azure Portal.
    • Rekord TXT z wartością Hostasuid i wartością identyfikatora weryfikacji wygenerowanego przez platformę Azure i udostępniony przez witrynę Azure Portal.

    Upewnij się, że zmiany są zapisywane poprawnie w witrynie internetowej rejestratora domen. Niektóre witryny internetowe rejestratora wymagają dwuetapowego procesu zapisywania rekordów domeny: co najmniej jeden rekord jest zapisywany indywidualnie, a następnie aktualizowany jest rejestracja domeny przy użyciu oddzielnego przycisku.

  10. Wróć do bloku Domeny niestandardowe w witrynie Azure Portal. Wybierz pozycję Dodaj domenę niestandardową. Wybierz opcję Rekord A. Podaj domenę i wybierz pozycję Weryfikuj. Jeśli rekordy domeny są poprawne i propagowane przez Internet, portal umożliwia wybranie przycisku Dodaj domenę niestandardową.

    Propagacja zmian rejestracji domeny na serwerach nazw domen internetowych (DNS) po przetworzeniu ich przez rejestratora domen może potrwać kilka dni. Jeśli rekordy domeny nie są aktualizowane w ciągu trzech dni roboczych, upewnij się, że rekordy są poprawnie ustawione u rejestratora domen i skontaktuj się z pomocą techniczną.

  11. W bloku Domeny niestandardowe stan SSL dla domeny jest oznaczony jako Not Secure. Wybierz link Dodaj powiązanie. Wybierz certyfikat HTTPS lokacji z magazynu kluczy dla powiązania domeny niestandardowej.

  12. W programie Visual Studio otwórz plik ustawień aplikacji projektu Server (appsettings.json lub appsettings.Production.json). Identity W konfiguracji serwera dodaj następującą Key sekcję. Określ podmiot certyfikatu z podpisem Name własnym dla klucza. W poniższym przykładzie nazwa pospolita certyfikatu przypisana w magazynie kluczy to IdentityServerSigning, co daje podmiot :CN=IdentityServerSigning

    "IdentityServer": {
    
      ...
    
      "Key": {
        "Type": "Store",
        "StoreName": "My",
        "StoreLocation": "CurrentUser",
        "Name": "CN=IdentityServerSigning"
      }
    },
    
  13. W programie Visual Studio utwórz profil publikowania usługi aplikacja systemu Azure dla projektu Server. Na pasku menu wybierz pozycję: Build Publish>>New>Azure> aplikacja systemu Azure Service (Windows or Linux). Gdy program Visual Studio jest połączony z subskrypcją platformy Azure, możesz ustawić widok zasobów platformy Azure według typu zasobu. Przejdź do listy Aplikacja internetowa, aby znaleźć usługę App Service dla aplikacji i wybrać ją. Wybierz Zakończ.

  14. Gdy program Visual Studio powróci do okna Publikowanie , zostaną automatycznie wykryte zależności magazynu kluczy i usługi bazy danych programu SQL Server.

    Dla usługi Key Vault nie są wymagane żadne zmiany konfiguracji ustawień domyślnych.

    W celach testowych lokalna baza danych SQLite aplikacji, która jest domyślnie skonfigurowana przez Blazor szablon, można wdrożyć z aplikacją bez dodatkowej konfiguracji. Konfigurowanie innej bazy danych dla Identity serwera w środowisku produkcyjnym wykracza poza zakres tego artykułu. Aby uzyskać więcej informacji, zobacz zasoby bazy danych w następujących zestawach dokumentacji:

  15. Wybierz link Edytuj w obszarze nazwy profilu wdrożenia w górnej części okna. Zmień docelowy adres URL na adres URL domeny niestandardowej witryny (na przykład https://www.contoso.com). Zapisz ustawienia.

  16. Opublikuj aplikację. Program Visual Studio otwiera okno przeglądarki i żąda witryny w swojej domenie niestandardowej.

Dokumentacja platformy Azure zawiera dodatkowe szczegóły dotyczące korzystania z usług platformy Azure i domen niestandardowych z powiązaniem TLS w usłudze App Service, w tym informacje na temat używania rekordów CNAME zamiast rekordów A. Aby uzyskać więcej informacji, zobacz następujące zasoby:

Zalecamy użycie nowego okna przeglądarki trybu prywatnego (na przykład trybu InPrivate przeglądarki Microsoft Edge lub trybu Incognito w przeglądarce Google Chrome) dla każdego testu aplikacji po zmianie aplikacji, konfiguracji aplikacji lub usług platformy Azure w witrynie Azure Portal. Utrzymujące cookiesię elementy z poprzedniego przebiegu testu mogą spowodować niepowodzenie uwierzytelniania lub autoryzacji podczas testowania lokacji nawet wtedy, gdy konfiguracja lokacji jest poprawna. Aby uzyskać więcej informacji na temat konfigurowania programu Visual Studio pod kątem otwierania nowego okna przeglądarki prywatnej dla każdego przebiegu testu, zobacz Cookiesekcję s i dane witryny.

Gdy konfiguracja usługi App Service zostanie zmieniona w witrynie Azure Portal, aktualizacje będą obowiązywać szybko, ale nie są natychmiastowe. Czasami należy poczekać krótki okres na ponowne uruchomienie usługi App Service w celu wprowadzenia zmiany konfiguracji.

Jeśli rozwiąże problem z ładowaniem Identity certyfikatu podpisywania klucza serwera, wykonaj następujące polecenie w powłoce poleceń programu PowerShell Kudu w witrynie Azure Portal. Polecenie zawiera listę certyfikatów, do których aplikacja może uzyskać dostęp z CurrentUser>My magazynu certyfikatów. Dane wyjściowe obejmują podmioty certyfikatu i odciski palca przydatne podczas debugowania aplikacji:

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

Rozwiązywanie problemów

Rejestrowanie

Aby włączyć rejestrowanie debugowania lub śledzenia na potrzeby Blazor WebAssembly uwierzytelniania, zobacz sekcję Rejestrowanie uwierzytelniania po stronie klienta ASP.NET Core Blazor z selektorem wersji artykułu ustawionym na ASP.NET Core 7.0 lub nowszym.

Typowe błędy

  • Błędna konfiguracja aplikacji lub Identity dostawcy (IP)

    Najczęstsze błędy są spowodowane nieprawidłową konfiguracją. Poniżej przedstawiono kilka przykładów:

    • W zależności od wymagań scenariusza brakujący lub niepoprawny urząd, wystąpienie, identyfikator dzierżawy, domena dzierżawy, identyfikator klienta lub identyfikator URI przekierowania uniemożliwia aplikacji uwierzytelnianie klientów.
    • Nieprawidłowe zakresy żądań uniemożliwiają klientom uzyskiwanie dostępu do punktów końcowych internetowego interfejsu API serwera.
    • Nieprawidłowe lub brakujące uprawnienia interfejsu API serwera uniemożliwiają klientom uzyskiwanie dostępu do punktów końcowych internetowego interfejsu API serwera.
    • Uruchamianie aplikacji na innym porcie niż jest skonfigurowane w identyfikatorze URI przekierowania rejestracji aplikacji adresu IP. Należy pamiętać, że port nie jest wymagany dla identyfikatora Entra firmy Microsoft i aplikacji działającej localhost na adresie testowania programowania, ale konfiguracja portu aplikacji i port, na którym działa aplikacja, musi być zgodna z adresami innychlocalhost niż.

    Sekcje konfiguracji tego artykułu zawierają przykłady prawidłowej konfiguracji. Dokładnie sprawdź każdą sekcję artykułu, aby wyszukać błędną konfigurację aplikacji i adresu IP.

    Jeśli konfiguracja jest poprawna:

    • Analizowanie dzienników aplikacji.

    • Sprawdź ruch sieciowy między aplikacją kliencka a adresem IP lub aplikacją serwera przy użyciu narzędzi deweloperskich przeglądarki. Często dokładny komunikat o błędzie lub komunikat zawierający wskazówki dotyczące przyczyn problemu jest zwracany do klienta przez adres IP lub aplikację serwera po wykonaniu żądania. Narzędzia programistyczne wskazówki można znaleźć w następujących artykułach:

    • W przypadku wersji Blazor , w których JSjest używany token internetowy ON (JWT), zdekoduj zawartość tokenu używanego do uwierzytelniania klienta lub uzyskiwania dostępu do internetowego interfejsu API serwera w zależności od tego, gdzie występuje problem. Aby uzyskać więcej informacji, zobacz Inspekcja zawartości tokenu internetowego JSON (JWT).

    Zespół dokumentacji odpowiada na opinie dotyczące dokumentów i usterek w artykułach (otwórz problem z sekcji Ta strona opinii), ale nie może zapewnić pomocy technicznej dotyczącej produktów. Dostępnych jest kilka publicznych forów pomocy technicznej, które ułatwiają rozwiązywanie problemów z aplikacją. Zalecamy:

    Poprzednie fora nie należą do firmy Microsoft ani nie są kontrolowane przez firmę Microsoft.

    W przypadku raportów usterek struktury niezwiązanych z zabezpieczeniami, niewrażliwych i nieufnych, otwórz problem z jednostką produktu ASP.NET Core. Nie otwieraj problemu z jednostką produktu, dopóki nie zbadasz dokładnie przyczyny problemu i nie możesz go rozwiązać samodzielnie i z pomocą społeczności na publicznym forum pomocy technicznej. Jednostka produktu nie może rozwiązywać problemów z poszczególnymi aplikacjami, które są uszkodzone z powodu prostej błędnej konfiguracji lub przypadków użycia obejmujących usługi innych firm. Jeśli raport ma charakter poufny lub opisuje potencjalną lukę w zabezpieczeniach produktu, którą mogą wykorzystać osoby atakujące, zobacz Raportowanie problemów z zabezpieczeniami i usterek (dotnet/aspnetcorerepozytorium GitHub).

  • Nieautoryzowany klient dla identyfikatora ME

    info: Autoryzacja Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2] nie powiodła się. Te wymagania nie zostały spełnione: DenyAnonymousAuthorizationRequirement: Wymaga uwierzytelnionego użytkownika.

    Błąd wywołania zwrotnego logowania z identyfikatora ME-ID:

    • Błąd: unauthorized_client
    • Opis: AADB2C90058: The provided application is not configured to allow public clients.

    Aby naprawić ten błąd:

    1. W witrynie Azure Portal uzyskaj dostęp do manifestu aplikacji.
    2. allowPublicClient Ustaw atrybut na null lub true.

Cookies i dane lokacji

Cookiedane s i lokacji mogą być utrwalane w aktualizacjach aplikacji i zakłócać testowanie i rozwiązywanie problemów. Wyczyść następujące informacje podczas wprowadzania zmian w kodzie aplikacji, zmiany konta użytkownika w dostawcy lub zmiany konfiguracji aplikacji dostawcy:

  • Logowanie cookieużytkownika
  • Aplikacje cookie
  • Buforowane i przechowywane dane lokacji

Jednym z podejść do zapobiegania utrzymującym cookiesię danym lokacji i testowaniu jest:

  • Konfigurowanie przeglądarki
    • Użyj przeglądarki do testowania, które można skonfigurować, aby usuwać wszystkie cookie dane witryny i za każdym razem, gdy przeglądarka jest zamknięta.
    • Upewnij się, że przeglądarka jest zamknięta ręcznie lub przez środowisko IDE w celu zmiany konfiguracji aplikacji, użytkownika testowego lub dostawcy.
  • Użyj polecenia niestandardowego, aby otworzyć przeglądarkę w trybie InPrivate lub Incognito w programie Visual Studio:
    • Otwórz okno dialogowe Przeglądaj za pomocą z przycisku Uruchom programu Visual Studio.
    • Kliknij przycisk Dodaj.
    • Podaj ścieżkę do przeglądarki w polu Program . Następujące ścieżki wykonywalne to typowe lokalizacje instalacji systemu Windows 10. Jeśli przeglądarka jest zainstalowana w innej lokalizacji lub nie używasz systemu Windows 10, podaj ścieżkę do pliku wykonywalnego przeglądarki.
      • 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
    • W polu Argumenty podaj opcję wiersza polecenia używaną przez przeglądarkę do otwierania w trybie InPrivate lub Incognito. Niektóre przeglądarki wymagają adresu URL aplikacji.
      • Microsoft Edge: użyj polecenia -inprivate.
      • Google Chrome: użyj symbolu --incognito --new-window {URL}zastępczego , gdzie symbol zastępczy {URL} to adres URL do otwarcia (na przykład https://localhost:5001).
      • Mozilla Firefox: użyj symbolu -private -url {URL}zastępczego , gdzie symbol zastępczy {URL} jest adresem URL do otwarcia (na przykład https://localhost:5001).
    • Podaj nazwę w polu Przyjazna nazwa . Na przykład Firefox Auth Testing.
    • Wybierz przycisk OK.
    • Aby uniknąć konieczności wybierania profilu przeglądarki dla każdej iteracji testowania za pomocą aplikacji, ustaw profil jako domyślny przy użyciu przycisku Ustaw jako domyślny .
    • Upewnij się, że przeglądarka jest zamknięta przez środowisko IDE w celu zmiany konfiguracji aplikacji, użytkownika testowego lub dostawcy.

Uaktualnienia aplikacji

Działająca aplikacja może zakończyć się niepowodzeniem natychmiast po uaktualnieniu zestawu .NET Core SDK na komputerze deweloperskim lub zmianie wersji pakietów w aplikacji. W niektórych przypadkach niespójne pakiety mogą spowodować przerwanie aplikacji podczas przeprowadzania głównych uaktualnień. Większość z tych problemów można rozwiązać, wykonując następujące instrukcje:

  1. Wyczyść pamięć podręczną pakietów NuGet systemu lokalnego, wykonując polecenie dotnet nuget locals all --clear z powłoki poleceń.
  2. Usuń foldery i obj foldery bin projektu.
  3. Przywracanie i ponowne kompilowanie projektu.
  4. Usuń wszystkie pliki w folderze wdrażania na serwerze przed ponownym wdrożeniem aplikacji.

Uwaga

Korzystanie z wersji pakietów niezgodnych z platformą docelową aplikacji nie jest obsługiwane. Aby uzyskać informacje na temat pakietu, użyj galerii NuGet lub Eksploratora pakietów FuGet.

Server Uruchamianie aplikacji

Podczas testowania i rozwiązywania problemów z hostowanym Blazor WebAssemblyrozwiązaniem upewnij się, że używasz aplikacji z Server projektu.

Sprawdzanie użytkownika

Poniższy User składnik może być używany bezpośrednio w aplikacjach lub służyć jako podstawa dalszego dostosowywania.

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

Sprawdzanie zawartości tokenu internetowego JSON (JWT)

Aby zdekodować JStoken internetowy ON (JWT), użyj narzędzia jwt.ms firmy Microsoft. Wartości w interfejsie użytkownika nigdy nie opuszczają przeglądarki.

Przykład zakodowany JWT (skrócony do wyświetlania):

eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ilg1ZVhrNHh5b2pORnVtMWtsMll0djhkbE5QNC1j ... bQdHBHGcQQRbW7Wmo6SWYG4V_bU55Ug_PW4pLPr20tTS8Ct7_uwy9DWrzCMzpD-EiwT5IjXwlGX3IXVjHIlX50IVIydBoPQtadvT7saKo1G5Jmutgq41o-dmz6-yBMKV2_nXA25Q

Przykładowy kod JWT dekodowany przez narzędzie dla aplikacji, która uwierzytelnia się w usłudze Azure AAD B2C:

{
  "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]

Dodatkowe zasoby