Sdílet prostřednictvím


ASP.NET Core Blazor WebAssembly – další scénáře zabezpečení

Poznámka:

Toto není nejnovější verze tohoto článku. Aktuální verzi najdete ve verzi .NET 8 tohoto článku.

Důležité

Tyto informace se týkají předběžného vydání produktu, který může být podstatně změněn před komerčním vydáním. Microsoft neposkytuje žádné záruky, výslovné ani předpokládané, týkající se zde uváděných informací.

Aktuální verzi najdete ve verzi .NET 8 tohoto článku.

Tento článek popisuje další scénáře zabezpečení pro Blazor WebAssembly aplikace.

Připojení tokenů k odchozím požadavkům

AuthorizationMessageHandlerDelegatingHandler slouží ke zpracování přístupovýchtokench Tokeny se získávají pomocí IAccessTokenProvider služby, která je zaregistrovaná architekturou. Pokud se token nedá získat, vyvolá se chyba AccessTokenNotAvailableException . AccessTokenNotAvailableException má metodu Redirect , která pomocí AccessTokenResult.InteractiveRequestUrl dané metody AccessTokenResult.InteractionOptions povolí aktualizaci přístupového tokenu.

Pro usnadnění přístupu poskytuje BaseAddressAuthorizationMessageHandler architektura předkonfigurovanou základní adresu aplikace jako autorizovanou adresu URL. Přístupové tokeny se přidají jenom v případech, kdy je identifikátor URI požadavku v rámci základního identifikátoru URI aplikace. Pokud identifikátory URI odchozích požadavků nejsou v základním identifikátoru URI aplikace, použijte vlastní třídu (doporučeno) nebo nakonfigurujteAuthorizationMessageHandler .AuthorizationMessageHandler

Poznámka:

Kromě konfigurace klientské aplikace pro přístup k rozhraní API serveru musí rozhraní API serveru také povolit požadavky mezi zdroji (CORS), pokud se klient a server nenachází na stejnou základní adresu. Další informace o konfiguraci CORS na straně serveru najdete v části Sdílení prostředků mezi zdroji (CORS) dále v tomto článku.

V následujícím příkladu:

V následujícím příkladu HttpClientFactoryServiceCollectionExtensions.AddHttpClient je rozšíření v Microsoft.Extensions.Http. Přidejte balíček do aplikace, která na něj ještě neodkazuje.

Poznámka:

Pokyny k přidávání balíčků do aplikací .NET najdete v článcích v části Instalace a správa balíčků na webu Pracovní postup používání balíčků (dokumentace k NuGetu). Ověřte správné verze balíčků na NuGet.org.

using System.Net.Http;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

...

builder.Services.AddHttpClient("WebAPI", 
        client => client.BaseAddress = new Uri("https://api.contoso.com/v1.0"))
    .AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();

builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>()
    .CreateClient("WebAPI"));

U hostovaného řešení založeného Blazor WebAssembly na šabloně projektu jsou identifikátory URI požadavků ve výchozím nastavení v základním identifikátoru URI aplikace.Blazor IWebAssemblyHostEnvironment.BaseAddress Proto se k HttpClient.BaseAddress aplikaci vygenerované ze šablony projektu přiřadí (new Uri(builder.HostEnvironment.BaseAddress)).

Nakonfigurované HttpClient se používá k provádění autorizovaných try-catch požadavků pomocí vzoru:

@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@inject HttpClient Http

...

protected override async Task OnInitializedAsync()
{
    try
    {
        var examples = 
            await Http.GetFromJsonAsync<ExampleType[]>("ExampleAPIMethod");

        ...
    }
    catch (AccessTokenNotAvailableException exception)
    {
        exception.Redirect();
    }
}

Scénáře požadavků na vlastní ověřování

Následující scénáře ukazují, jak přizpůsobit žádosti o ověření a jak získat cestu přihlášení z možností ověřování.

Přizpůsobení procesu přihlášení

Spravujte další parametry pro žádost o přihlášení pomocí následujících metod jednou nebo vícekrát v nové instanci InteractiveRequestOptions:

V následujícím LoginDisplay příkladu komponenty se do žádosti o přihlášení přidají další parametry:

  • prompt je nastavena na loginhodnotu : Vynutí, aby uživatel zadal své přihlašovací údaje pro danou žádost a neguje jednotné přihlašování.
  • loginHintje nastavena na peter@contoso.com: Předvyplní pole uživatelské jméno/e-mailová adresa přihlašovací stránky pro uživatele.peter@contoso.com Aplikace tento parametr často používají při opětovném ověřování a už extrahovali uživatelské jméno z předchozího přihlášení pomocí preferred_username deklarace identity.

Shared/LoginDisplay.razor:

@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@inject NavigationManager Navigation

<AuthorizeView>
    <Authorized>
        Hello, @context.User.Identity?.Name!
        <button @onclick="BeginLogOut">Log out</button>
    </Authorized>
    <NotAuthorized>
        <button @onclick="BeginLogIn">Log in</button>
    </NotAuthorized>
</AuthorizeView>

@code{
    public void BeginLogOut()
    {
        Navigation.NavigateToLogout("authentication/logout");
    }

    public void BeginLogIn()
    {
        InteractiveRequestOptions requestOptions =
            new()
            {
                Interaction = InteractionType.SignIn,
                ReturnUrl = Navigation.Uri,
            };

        requestOptions.TryAddAdditionalParameter("prompt", "login");
        requestOptions.TryAddAdditionalParameter("loginHint", "peter@contoso.com");

        Navigation.NavigateToLogin("authentication/login", requestOptions);
    }
}

Další informace naleznete v následujících zdrojích:

Přizpůsobení možností před interaktivním získáním tokenu

Pokud dojde k nějaké AccessTokenNotAvailableException chybě, spravujte další parametry pro požadavek nového přístupového tokenu zprostředkovatele identity pomocí následujících metod jednou nebo vícekrát v nové instanci InteractiveRequestOptions:

V následujícím příkladu, který získává JSdata ON prostřednictvím webového rozhraní API, se do požadavku přesměrování přidají další parametry, pokud přístupový token není k dispozici (AccessTokenNotAvailableException vyvolá se):

  • prompt je nastavena na loginhodnotu : Vynutí, aby uživatel zadal své přihlašovací údaje pro danou žádost a neguje jednotné přihlašování.
  • loginHintje nastavena na peter@contoso.com: Předvyplní pole uživatelské jméno/e-mailová adresa přihlašovací stránky pro uživatele.peter@contoso.com Aplikace tento parametr často používají při opětovném ověřování a už extrahovali uživatelské jméno z předchozího přihlášení pomocí preferred_username deklarace identity.
try
{
    var examples = await Http.GetFromJsonAsync<ExampleType[]>("ExampleAPIMethod");

    ...
}
catch (AccessTokenNotAvailableException ex)
{
    ex.Redirect(requestOptions => {
        requestOptions.TryAddAdditionalParameter("prompt", "login");
        requestOptions.TryAddAdditionalParameter("loginHint", "peter@contoso.com");
    });
}

Předchozí příklad předpokládá, že:

Další informace naleznete v následujících zdrojích:

Přizpůsobení možností při použití IAccessTokenProvider

Pokud získání tokenu selže při použití IAccessTokenProvider, spravujte další parametry pro požadavek nového přístupového tokenu zprostředkovatele identity jednou nebo vícekrát v nové instanci InteractiveRequestOptions:

V následujícím příkladu, který se pokusí získat přístupový token pro uživatele, se do žádosti o přihlášení přidají další parametry, pokud se při volání pokusu o získání tokenu nezdaří TryGetToken :

  • prompt je nastavena na loginhodnotu : Vynutí, aby uživatel zadal své přihlašovací údaje pro danou žádost a neguje jednotné přihlašování.
  • loginHintje nastavena na peter@contoso.com: Předvyplní pole uživatelské jméno/e-mailová adresa přihlašovací stránky pro uživatele.peter@contoso.com Aplikace tento parametr často používají při opětovném ověřování a už extrahovali uživatelské jméno z předchozího přihlášení pomocí preferred_username deklarace identity.
var tokenResult = await TokenProvider.RequestAccessToken(
    new AccessTokenRequestOptions
    {
        Scopes = new[] { ... }
    });

if (!tokenResult.TryGetToken(out var token))
{
    tokenResult.InteractionOptions.TryAddAdditionalParameter("prompt", "login");
    tokenResult.InteractionOptions.TryAddAdditionalParameter("loginHint", 
        "peter@contoso.com");

    Navigation.NavigateToLogin(accessTokenResult.InteractiveRequestUrl, 
        accessTokenResult.InteractionOptions);
}

Předchozí příklad předpokládá:

Další informace naleznete v následujících zdrojích:

Odhlášení s vlastní návratovou adresou URL

Následující příklad odhlásí uživatele a vrátí uživatele do koncového /goodbye bodu:

Navigation.NavigateToLogout("authentication/logout", "goodbye");

Získání přihlašovací cesty z možností ověřování

Získejte nakonfigurovanou přihlašovací cestu z RemoteAuthenticationOptions:

var loginPath = 
    RemoteAuthOptions.Get(Options.DefaultName).AuthenticationPaths.LogInPath;

Předchozí příklad předpokládá:

Vlastní AuthorizationMessageHandler třída

Tyto pokyny v této části se doporučují pro klientské aplikace, které odesílají odchozí požadavky na identifikátory URI, které nejsou v rámci základního identifikátoru URI aplikace.

V následujícím příkladu se vlastní třída rozšiřuje AuthorizationMessageHandler pro použití jako DelegatingHandler for an HttpClient. ConfigureHandler nakonfiguruje tuto obslužnou rutinu tak, aby autorizovala odchozí požadavky HTTP pomocí přístupového tokenu. Přístupový token je připojen pouze v případě, že alespoň jedna z autorizovaných adres URL je základem identifikátoru URI požadavku (HttpRequestMessage.RequestUri).

using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

public class CustomAuthorizationMessageHandler : AuthorizationMessageHandler
{
    public CustomAuthorizationMessageHandler(IAccessTokenProvider provider, 
        NavigationManager navigation)
        : base(provider, navigation)
    {
        ConfigureHandler(
            authorizedUrls: new[] { "https://api.contoso.com/v1.0" },
            scopes: new[] { "example.read", "example.write" });
    }
}

V předchozím kódu nejsou obory example.read a example.write obecné příklady, které by odrážely platné obory pro konkrétního poskytovatele.

Program V souboru je CustomAuthorizationMessageHandler registrována jako přechodná služba a je nakonfigurována jako DelegatingHandler pro odchozí HttpResponseMessage instance vytvořené pojmenovanou HttpClient.

V následujícím příkladu HttpClientFactoryServiceCollectionExtensions.AddHttpClient je rozšíření v Microsoft.Extensions.Http. Přidejte balíček do aplikace, která na něj ještě neodkazuje.

Poznámka:

Pokyny k přidávání balíčků do aplikací .NET najdete v článcích v části Instalace a správa balíčků na webu Pracovní postup používání balíčků (dokumentace k NuGetu). Ověřte správné verze balíčků na NuGet.org.

builder.Services.AddTransient<CustomAuthorizationMessageHandler>();

builder.Services.AddHttpClient("WebAPI",
        client => client.BaseAddress = new Uri("https://api.contoso.com/v1.0"))
    .AddHttpMessageHandler<CustomAuthorizationMessageHandler>();

Poznámka:

V předchozím příkladu CustomAuthorizationMessageHandlerDelegatingHandler je registrován jako přechodná služba pro AddHttpMessageHandler. Přechodná registrace se doporučuje pro IHttpClientFactory, která spravuje vlastní obory DI. Další informace naleznete v následujících zdrojích:

U hostovaného Blazor řešení založenéhoBlazor WebAssemblyna šabloněIWebAssemblyHostEnvironment.BaseAddress projektu () je standardně přiřazeno HttpClient.BaseAddress (new Uri(builder.HostEnvironment.BaseAddress)).

Nakonfigurované HttpClient se používá k provádění autorizovaných požadavků pomocí try-catch vzoru. Kde se klient vytvoří pomocí CreateClient (Microsoft.Extensions.Http balíčku), HttpClient zadává se instance, které při provádění požadavků na serverové rozhraní API obsahují přístupové tokeny. Pokud je identifikátor URI požadavku relativním identifikátorem URI, protože je v následujícím příkladu (ExampleAPIMethod), zkombinuje se s BaseAddress tím, kdy klientská aplikace požadavek provede:

@inject IHttpClientFactory ClientFactory

...

@code {
    protected override async Task OnInitializedAsync()
    {
        try
        {
            var client = ClientFactory.CreateClient("WebAPI");

            var examples = 
                await client.GetFromJsonAsync<ExampleType[]>("ExampleAPIMethod");

            ...
        }
        catch (AccessTokenNotAvailableException exception)
        {
            exception.Redirect();
        }
    }
}

Konfigurace AuthorizationMessageHandler

AuthorizationMessageHandler pomocí metody lze nakonfigurovat s autorizovanými adresami URL, obory a návratovou adresou URL ConfigureHandler . ConfigureHandler nakonfiguruje obslužnou rutinu tak, aby autorizovala odchozí požadavky HTTP pomocí přístupového tokenu. Přístupový token je připojen pouze v případě, že alespoň jedna z autorizovaných adres URL je základem identifikátoru URI požadavku (HttpRequestMessage.RequestUri). Pokud je identifikátor URI požadavku relativní identifikátor URI, zkombinuje se s identifikátorem BaseAddress.

V následujícím příkladu AuthorizationMessageHandlerProgram nakonfiguruje souborHttpClient:

using System.Net.Http;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

...

builder.Services.AddScoped(sp => new HttpClient(
    sp.GetRequiredService<AuthorizationMessageHandler>()
    .ConfigureHandler(
        authorizedUrls: new[] { "https://api.contoso.com/v1.0" },
        scopes: new[] { "example.read", "example.write" }))
    {
        BaseAddress = new Uri("https://api.contoso.com/v1.0")
    });

V předchozím kódu nejsou obory example.read a example.write obecné příklady, které by odrážely platné obory pro konkrétního poskytovatele.

Pro hostované Blazor řešení založené na Blazor WebAssembly šabloněIWebAssemblyHostEnvironment.BaseAddress projektu je ve výchozím nastavení přiřazeno následující:

Zadaný HttpClient

Typový klient lze definovat, který zpracovává všechny obavy o získání http a tokenu v rámci jedné třídy.

WeatherForecastClient.cs:

using System.Net.Http.Json;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using static {ASSEMBLY NAME}.Data;

public class WeatherForecastClient
{
    private readonly HttpClient http;
    private WeatherForecast[]? forecasts;

    public WeatherForecastClient(HttpClient http)
    {
        this.http = http;
    }

    public async Task<WeatherForecast[]> GetForecastAsync()
    {
        try
        {
            forecasts = await http.GetFromJsonAsync<WeatherForecast[]>(
                "WeatherForecast");
        }
        catch (AccessTokenNotAvailableException exception)
        {
            exception.Redirect();
        }

        return forecasts ?? Array.Empty<WeatherForecast>();
    }
}
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using static {ASSEMBLY NAME}.Data;

public class WeatherForecastClient
{
    private readonly HttpClient http;
    private WeatherForecast[] forecasts;

    public WeatherForecastClient(HttpClient http)
    {
        this.http = http;
    }

    public async Task<WeatherForecast[]> GetForecastAsync()
    {
        try
        {
            forecasts = await http.GetFromJsonAsync<WeatherForecast[]>(
                "WeatherForecast");
        }
        catch (AccessTokenNotAvailableException exception)
        {
            exception.Redirect();
        }

        return forecasts ?? Array.Empty<WeatherForecast>();
    }
}

V předchozím příkladu WeatherForecast je typ statickou třídou, která obsahuje data předpovědi počasí. Zástupný symbol {ASSEMBLY NAME} je název sestavení aplikace (například using static BlazorSample.Data;).

V následujícím příkladu HttpClientFactoryServiceCollectionExtensions.AddHttpClient je rozšíření v Microsoft.Extensions.Http. Přidejte balíček do aplikace, která na něj ještě neodkazuje.

Poznámka:

Pokyny k přidávání balíčků do aplikací .NET najdete v článcích v části Instalace a správa balíčků na webu Pracovní postup používání balíčků (dokumentace k NuGetu). Ověřte správné verze balíčků na NuGet.org.

V souboru Program:

using System.Net.Http;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

...

builder.Services.AddHttpClient<WeatherForecastClient>(
        client => client.BaseAddress = new Uri("https://api.contoso.com/v1.0"))
    .AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();

U hostovaného Blazor řešení založenéhoBlazor WebAssemblyna šabloněIWebAssemblyHostEnvironment.BaseAddress projektu () je standardně přiřazeno HttpClient.BaseAddress (new Uri(builder.HostEnvironment.BaseAddress)).

V komponentě, která načítá data o počasí:

@inject WeatherForecastClient Client

...

protected override async Task OnInitializedAsync()
{
    forecasts = await Client.GetForecastAsync();
}

Konfigurace obslužné rutiny HttpClient

Obslužnou rutinu je možné dále nakonfigurovat ConfigureHandler pro odchozí požadavky HTTP.

V následujícím příkladu HttpClientFactoryServiceCollectionExtensions.AddHttpClient je rozšíření v Microsoft.Extensions.Http. Přidejte balíček do aplikace, která na něj ještě neodkazuje.

Poznámka:

Pokyny k přidávání balíčků do aplikací .NET najdete v článcích v části Instalace a správa balíčků na webu Pracovní postup používání balíčků (dokumentace k NuGetu). Ověřte správné verze balíčků na NuGet.org.

V souboru Program:

builder.Services.AddHttpClient<WeatherForecastClient>(
        client => client.BaseAddress = new Uri("https://api.contoso.com/v1.0"))
    .AddHttpMessageHandler(sp => sp.GetRequiredService<AuthorizationMessageHandler>()
    .ConfigureHandler(
        authorizedUrls: new [] { "https://api.contoso.com/v1.0" },
        scopes: new[] { "example.read", "example.write" }));

V předchozím kódu nejsou obory example.read a example.write obecné příklady, které by odrážely platné obory pro konkrétního poskytovatele.

Pro hostované Blazor řešení založené na Blazor WebAssembly šabloněIWebAssemblyHostEnvironment.BaseAddress projektu je ve výchozím nastavení přiřazeno následující:

Neověřené nebo neoprávněné požadavky webového rozhraní API v aplikaci se zabezpečeným výchozím klientem

Aplikace, která obvykle používá zabezpečené výchozí nastavení HttpClient , může také provádět neověřené nebo neoprávněné požadavky webového rozhraní API konfigurací pojmenovaného HttpClientrozhraní API .

V následujícím příkladu HttpClientFactoryServiceCollectionExtensions.AddHttpClient je rozšíření v Microsoft.Extensions.Http. Přidejte balíček do aplikace, která na něj ještě neodkazuje.

Poznámka:

Pokyny k přidávání balíčků do aplikací .NET najdete v článcích v části Instalace a správa balíčků na webu Pracovní postup používání balíčků (dokumentace k NuGetu). Ověřte správné verze balíčků na NuGet.org.

V souboru Program:

builder.Services.AddHttpClient("WebAPI.NoAuthenticationClient", 
    client => client.BaseAddress = new Uri("https://api.contoso.com/v1.0"));

U hostovaného Blazor řešení založenéhoBlazor WebAssemblyna šabloněIWebAssemblyHostEnvironment.BaseAddress projektu () je standardně přiřazeno HttpClient.BaseAddress (new Uri(builder.HostEnvironment.BaseAddress)).

Předchozí registrace je navíc k existující zabezpečené výchozí HttpClient registraci.

Komponenta HttpClient vytvoří z IHttpClientFactory (Microsoft.Extensions.Http balíčku) neověřené nebo neoprávněné požadavky:

@inject IHttpClientFactory ClientFactory

...

@code {
    protected override async Task OnInitializedAsync()
    {
        var client = ClientFactory.CreateClient("WebAPI.NoAuthenticationClient");

        var examples = await client.GetFromJsonAsync<ExampleType[]>(
            "ExampleNoAuthentication");

        ...
    }
}

Poznámka:

Kontroler v rozhraní API serveru v ExampleNoAuthenticationController předchozím příkladu není označen atributem[Authorize].

Rozhodnutí, jestli použít zabezpečeného klienta nebo nezabezpečeného klienta jako výchozí HttpClient instanci, je až na vývojáři. Jedním ze způsobů, jak toto rozhodnutí provést, je zvážit počet ověřených a neověřených koncových bodů, které kontaktují aplikace. Pokud je většina požadavků aplikace na zabezpečení koncových bodů rozhraní API, použijte ověřenou HttpClient instanci jako výchozí. V opačném případě zaregistrujte neověřenou HttpClient instanci jako výchozí.

Alternativním přístupem k použití je IHttpClientFactory vytvoření zadaného klienta pro neověřený přístup k anonymním koncovým bodům.

Vyžádání dalších přístupových tokenů

Přístupové tokeny lze získat ručně voláním IAccessTokenProvider.RequestAccessToken. V následujícím příkladu je pro výchozí HttpClientnastavení vyžadován další obor aplikace . Příklad knihovny MSAL (Microsoft Authentication Library) konfiguruje obor takto MsalProviderOptions:

V souboru Program:

builder.Services.AddMsalAuthentication(options =>
{
    ...

    options.ProviderOptions.AdditionalScopesToConsent.Add("{CUSTOM SCOPE 1}");
    options.ProviderOptions.AdditionalScopesToConsent.Add("{CUSTOM SCOPE 2}");
}

{CUSTOM SCOPE 2} Zástupné {CUSTOM SCOPE 1} symboly v předchozím příkladu jsou vlastní obory.

Poznámka:

AdditionalScopesToConsent Není možné zřídit delegovaná uživatelská oprávnění pro Microsoft Graph prostřednictvím uživatelského rozhraní souhlasu s ID Microsoft Entra, když uživatel poprvé používá aplikaci zaregistrovanou v Microsoft Azure. Další informace najdete v tématu Použití rozhraní Graph API s ASP.NET Core Blazor WebAssembly.

Metoda IAccessTokenProvider.RequestAccessToken poskytuje přetížení, které aplikaci umožňuje zřídit přístupový token s danou sadou oborů.

V komponentě Razor :

@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@inject IAccessTokenProvider TokenProvider

...

var tokenResult = await TokenProvider.RequestAccessToken(
    new AccessTokenRequestOptions
    {
        Scopes = new[] { "{CUSTOM SCOPE 1}", "{CUSTOM SCOPE 2}" }
    });

if (tokenResult.TryGetToken(out var token))
{
    ...
}

{CUSTOM SCOPE 2} Zástupné {CUSTOM SCOPE 1} symboly v předchozím příkladu jsou vlastní obory.

AccessTokenResult.TryGetToken Vrátí:

  • truetoken k použití.
  • false pokud se token nenačte.

Cross-Origin Resource Sharing (CORS)

Při odesílání přihlašovacích údajů (autorizačních cookies/hlaviček) u požadavků Authorization CORS musí být hlavička povolená zásadami CORS.

Následující zásady zahrnují konfiguraci pro:

  • Požádat o původ (http://localhost:5000, https://localhost:5001).
  • Libovolná metoda (sloveso).
  • Content-Type a Authorization záhlaví. Pokud chcete povolit vlastní záhlaví (například x-custom-header), vypsat záhlaví při volání WithHeaders.
  • Přihlašovací údaje nastavené kódem JavaScriptu na straně klienta (credentials vlastnost nastavená na include).
app.UseCors(policy => 
    policy.WithOrigins("http://localhost:5000", "https://localhost:5001")
        .AllowAnyMethod()
        .WithHeaders(HeaderNames.ContentType, HeaderNames.Authorization, 
            "x-custom-header")
        .AllowCredentials());

Hostované Blazor řešení založené na Blazor WebAssembly šabloně projektu používá stejnou základní adresu pro klientské a serverové aplikace. Klientská HttpClient.BaseAddress aplikace je ve výchozím nastavení nastavená na identifikátor URI builder.HostEnvironment.BaseAddress . Konfigurace CORS se nevyžaduje ve výchozí konfiguraci hostovaného Blazor řešení. Další klientské aplikace, které nejsou hostované projektem serveru a nesdílejí základní adresu serverové aplikace, vyžadují konfiguraci CORS v projektu serveru.

Další informace najdete v tématu Povolení požadavků mezi zdroji (CORS) v ASP.NET Core a součásti TESTeru požadavků HTTP ukázkové aplikace (Components/HTTPRequestTester.razor).

Zpracování chyb požadavků na token

Když jednostránkové aplikace (SPA) ověřuje uživatele pomocí OpenID Připojení (OIDC), stav ověřování se udržuje místně v rámci SPA a ve Identity zprostředkovateli (IP) ve formě relacecookie, která je nastavená v důsledku zadání přihlašovacích údajů uživatele.

Tokeny, které IP adresa generuje pro uživatele, jsou obvykle platné po krátkou dobu přibližně jednu hodinu, takže klientská aplikace musí pravidelně načítat nové tokeny. Jinak by se uživatel odhlásil po vypršení platnosti udělených tokenů. Ve většině případů OIDC můžou klienti OIDC zřizovat nové tokeny bez toho, aby se uživatel musel znovu ověřit díky stavu ověřování nebo relaci, která se uchovává v rámci IP adresy.

V některých případech klient nemůže získat token bez zásahu uživatele, například když se uživatel z nějakého důvodu explicitně odhlásí z IP adresy. K tomuto scénáři dochází v případě, že uživatel navštíví https://login.microsoftonline.com a odhlásí se. V těchto scénářích aplikace okamžitě neví, že se uživatel odhlásil. Jakýkoli token, který klient uchovává, už nemusí být platný. Klient také nemůže zřídit nový token bez zásahu uživatele po vypršení platnosti aktuálního tokenu.

Tyto scénáře nejsou specifické pro ověřování založené na tokenech. Jsou součástí povahy spA. Pokud je ověřování cookie odebráno, volání rozhraní API serveru se také nezdaří pomocí spa.cookie

Když aplikace provádí volání rozhraní API pro chráněné prostředky, musíte mít na paměti následující skutečnosti:

  • Pokud chcete zřídit nový přístupový token pro volání rozhraní API, může se stát, že se uživatel bude muset znovu ověřit.
  • I když má klient token, který se zdá být platný, může volání serveru selhat, protože token byl odvolán uživatelem.

Když aplikace požádá o token, existují dva možné výsledky:

  • Požadavek je úspěšný a aplikace má platný token.
  • Požadavek selže a aplikace musí znovu ověřit uživatele, aby získal nový token.

Pokud požadavek na token selže, musíte se rozhodnout, jestli chcete před přesměrováním uložit libovolný aktuální stav. Existuje několik přístupů k ukládání stavu s rostoucí úrovní složitosti:

  • Uložte aktuální stav stránky v úložišti relací. OnInitializedAsync Během metody životního cyklu (OnInitializedAsync) před pokračováním zkontrolujte, jestli je možné obnovit stav.
  • Přidejte parametr řetězce dotazu a použijte ho jako způsob, jak signalizovat aplikaci, že potřebuje znovu hydratovat dříve uložený stav.
  • Přidejte parametr řetězce dotazu s jedinečným identifikátorem pro ukládání dat do úložiště relací bez rizika kolizí s jinými položkami.

Uložení stavu aplikace před operací ověřování s úložištěm relací

Následující příklad ukazuje, jak:

  • Zachovat stav před přesměrováním na přihlašovací stránku.
  • Obnovení předchozího stavu po ověření pomocí parametru řetězce dotazu
...
@using System.Text.Json
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@inject IAccessTokenProvider TokenProvider
@inject IJSRuntime JS
@inject NavigationManager Navigation

<EditForm Model="User" OnSubmit="OnSaveAsync">
    <label>
        First Name: 
        <InputText @bind-Value="User!.Name" />
    </label>
    <label>
        Last Name: 
        <InputText @bind-Value="User!.LastName" />
    </label>
    <button type="submit">Save User</button>
</EditForm>

@code {
    public Profile User { get; set; } = new Profile();

    protected override async Task OnInitializedAsync()
    {
        var currentQuery = new Uri(Navigation.Uri).Query;

        if (currentQuery.Contains("state=resumeSavingProfile"))
        {
            var user = await JS.InvokeAsync<string>("sessionStorage.getItem",
                "resumeSavingProfile");

            if (!string.IsNullOrEmpty(user))
            {
                User = JsonSerializer.Deserialize<Profile>(user);
            }
        }
    }

    public async Task OnSaveAsync()
    {
        var http = new HttpClient();
        http.BaseAddress = new Uri(Navigation.BaseUri);

        var resumeUri = Navigation.Uri + $"?state=resumeSavingProfile";

        var tokenResult = await TokenProvider.RequestAccessToken(
            new AccessTokenRequestOptions
            {
                ReturnUrl = resumeUri
            });

        if (tokenResult.TryGetToken(out var token))
        {
            http.DefaultRequestHeaders.Add("Authorization", 
                $"Bearer {token.Value}");
            await http.PostAsJsonAsync("Save", User);
        }
        else
        {
            await JS.InvokeVoidAsync("sessionStorage.setItem", 
                "resumeSavingProfile", JsonSerializer.Serialize(User));
            Navigation.NavigateTo(tokenResult.InteractiveRequestUrl);
        }
    }

    public class Profile
    {
        public string? FirstName { get; set; }
        public string? LastName { get; set; }
    }
}

Uložení stavu aplikace před operací ověřování s úložištěm relací a kontejnerem stavu

Během operace ověřování existují případy, kdy chcete před přesměrováním prohlížeče na IP adresu uložit stav aplikace. To může být případ, kdy používáte kontejner stavu a chcete obnovit stav po úspěšném ověření. Vlastní objekt stavu ověřování můžete použít k zachování stavu specifického pro aplikaci nebo odkazu na něj a obnovení tohoto stavu po úspěšném dokončení operace ověřování. Následující příklad ukazuje přístup.

V aplikaci se vytvoří třída kontejneru stavu s vlastnostmi, které budou obsahovat hodnoty stavu aplikace. V následujícím příkladu se kontejner používá k zachování hodnoty čítače komponenty výchozí Blazor šablonyCounter projektu (Counter.razor). Metody serializace a deserializace kontejneru jsou založeny na System.Text.Json.

using System.Text.Json;

public class StateContainer
{
    public int CounterValue { get; set; }

    public string GetStateForLocalStorage()
    {
        return JsonSerializer.Serialize(this);
    }

    public void SetStateFromLocalStorage(string locallyStoredState)
    {
        var deserializedState = 
            JsonSerializer.Deserialize<StateContainer>(locallyStoredState);

        CounterValue = deserializedState.CounterValue;
    }
}

Komponenta Counter používá kontejner stavu k udržování currentCount hodnoty mimo komponentu:

@page "/counter"
@inject StateContainer State

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    protected override void OnInitialized()
    {
        if (State.CounterValue > 0)
        {
            currentCount = State.CounterValue;
        }
    }

    private void IncrementCount()
    {
        currentCount++;
        State.CounterValue = currentCount;
    }
}

Vytvořit z ApplicationAuthenticationStateRemoteAuthenticationState. Id Zadejte vlastnost, která slouží jako identifikátor místně uloženého stavu.

ApplicationAuthenticationState.cs:

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

public class ApplicationAuthenticationState : RemoteAuthenticationState
{
    public string? Id { get; set; }
}
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

public class ApplicationAuthenticationState : RemoteAuthenticationState
{
    public string Id { get; set; }
}

Komponenta Authentication (Authentication.razor) uloží a obnoví stav aplikace pomocí místního úložiště relací s metodami StateContainer serializace a deserializace a GetStateForLocalStorageSetStateFromLocalStorage:

@page "/authentication/{action}"
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@inject IJSRuntime JS
@inject StateContainer State

<RemoteAuthenticatorViewCore Action="Action"
                             TAuthenticationState="ApplicationAuthenticationState"
                             AuthenticationState="AuthenticationState"
                             OnLogInSucceeded="RestoreState"
                             OnLogOutSucceeded="RestoreState" />

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

    public ApplicationAuthenticationState AuthenticationState { get; set; } =
        new ApplicationAuthenticationState();

    protected override async Task OnInitializedAsync()
    {
        if (RemoteAuthenticationActions.IsAction(RemoteAuthenticationActions.LogIn,
            Action) ||
            RemoteAuthenticationActions.IsAction(RemoteAuthenticationActions.LogOut,
            Action))
        {
            AuthenticationState.Id = Guid.NewGuid().ToString();

            await JS.InvokeVoidAsync("sessionStorage.setItem",
                AuthenticationState.Id, State.GetStateForLocalStorage());
        }
    }

    private async Task RestoreState(ApplicationAuthenticationState state)
    {
        if (state.Id != null)
        {
            var locallyStoredState = await JS.InvokeAsync<string>(
                "sessionStorage.getItem", state.Id);

            if (locallyStoredState != null)
            {
                State.SetStateFromLocalStorage(locallyStoredState);
                await JS.InvokeVoidAsync("sessionStorage.removeItem", state.Id);
            }
        }
    }
}
@page "/authentication/{action}"
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@inject IJSRuntime JS
@inject StateContainer State

<RemoteAuthenticatorViewCore Action="Action"
                             TAuthenticationState="ApplicationAuthenticationState"
                             AuthenticationState="AuthenticationState"
                             OnLogInSucceeded="RestoreState"
                             OnLogOutSucceeded="RestoreState" />

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

    public ApplicationAuthenticationState AuthenticationState { get; set; } =
        new ApplicationAuthenticationState();

    protected override async Task OnInitializedAsync()
    {
        if (RemoteAuthenticationActions.IsAction(RemoteAuthenticationActions.LogIn,
            Action) ||
            RemoteAuthenticationActions.IsAction(RemoteAuthenticationActions.LogOut,
            Action))
        {
            AuthenticationState.Id = Guid.NewGuid().ToString();

            await JS.InvokeVoidAsync("sessionStorage.setItem",
                AuthenticationState.Id, State.GetStateForLocalStorage());
        }
    }

    private async Task RestoreState(ApplicationAuthenticationState state)
    {
        if (state.Id != null)
        {
            var locallyStoredState = await JS.InvokeAsync<string>(
                "sessionStorage.getItem", state.Id);

            if (locallyStoredState != null)
            {
                State.SetStateFromLocalStorage(locallyStoredState);
                await JS.InvokeVoidAsync("sessionStorage.removeItem", state.Id);
            }
        }
    }
}

V tomto příkladu se k ověřování používá Microsoft Entra (ME-ID). V souboru Program:

  • Je ApplicationAuthenticationState nakonfigurovaný jako typ knihovny MSAL (Microsoft Authentication Library). RemoteAuthenticationState
  • Kontejner stavu je zaregistrovaný v kontejneru služby.
builder.Services.AddMsalAuthentication<ApplicationAuthenticationState>(options =>
{
    builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
});

builder.Services.AddSingleton<StateContainer>();

Přizpůsobení tras aplikací

Ve výchozím nastavení Microsoft.AspNetCore.Components.WebAssembly.Authentication knihovna používá trasy uvedené v následující tabulce pro reprezentaci různých stavů ověřování.

Postup Účel
authentication/login Aktivuje operaci přihlášení.
authentication/login-callback Zpracovává výsledek jakékoli operace přihlášení.
authentication/login-failed Zobrazí chybové zprávy, když operace přihlášení z nějakého důvodu selže.
authentication/logout Aktivuje operaci odhlášení.
authentication/logout-callback Zpracovává výsledek operace odhlášení.
authentication/logout-failed Zobrazí chybové zprávy, když operace odhlášení z nějakého důvodu selže.
authentication/logged-out Označuje, že se uživatel úspěšně odhlásí.
authentication/profile Aktivuje operaci pro úpravu profilu uživatele.
authentication/register Aktivuje operaci pro registraci nového uživatele.

Trasy uvedené v předchozí tabulce jsou konfigurovatelné prostřednictvím RemoteAuthenticationOptions<TRemoteAuthenticationProviderOptions>.AuthenticationPaths. Při nastavování možností poskytnutí vlastních tras ověřte, že aplikace má trasu, která zpracovává jednotlivé cesty.

V následujícím příkladu jsou všechny cesty předponou /security.

Authentication součást (Authentication.razor):

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

<RemoteAuthenticatorView Action="@Action" />

@code{
    [Parameter]
    public string? Action { get; set; }
}
@page "/security/{action}"
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication

<RemoteAuthenticatorView Action="@Action" />

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

V souboru Program:

builder.Services.AddApiAuthorization(options => { 
    options.AuthenticationPaths.LogInPath = "security/login";
    options.AuthenticationPaths.LogInCallbackPath = "security/login-callback";
    options.AuthenticationPaths.LogInFailedPath = "security/login-failed";
    options.AuthenticationPaths.LogOutPath = "security/logout";
    options.AuthenticationPaths.LogOutCallbackPath = "security/logout-callback";
    options.AuthenticationPaths.LogOutFailedPath = "security/logout-failed";
    options.AuthenticationPaths.LogOutSucceededPath = "security/logged-out";
    options.AuthenticationPaths.ProfilePath = "security/profile";
    options.AuthenticationPaths.RegisterPath = "security/register";
});

Pokud požadavek volá zcela jiné cesty, nastavte trasy, jak je popsáno výše, a vykreslujte RemoteAuthenticatorView je explicitním parametrem akce:

@page "/register"

<RemoteAuthenticatorView Action="RemoteAuthenticationActions.Register" />

Pokud se tak rozhodnete, můžete uživatelské rozhraní rozdělit na různé stránky.

Přizpůsobení uživatelského rozhraní ověřování

RemoteAuthenticatorView obsahuje výchozí sadu fragmentů uživatelského rozhraní pro každý stav ověřování. Každý stav lze přizpůsobit předáním vlastního RenderFragmentstavu . Chcete-li přizpůsobit zobrazený text během počátečního procesu přihlášení, můžete změnit RemoteAuthenticatorView následujícím způsobem.

Authentication součást (Authentication.razor):

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

<RemoteAuthenticatorView Action="@Action">
    <LoggingIn>
        You are about to be redirected to https://login.microsoftonline.com.
    </LoggingIn>
</RemoteAuthenticatorView>

@code{
    [Parameter]
    public string? Action { get; set; }
}
@page "/security/{action}"
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication

<RemoteAuthenticatorView Action="@Action">
    <LoggingIn>
        You are about to be redirected to https://login.microsoftonline.com.
    </LoggingIn>
</RemoteAuthenticatorView>

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

Obsahuje RemoteAuthenticatorView jeden fragment, který lze použít pro každou trasu ověřování uvedenou v následující tabulce.

Postup Fragment
authentication/login <LoggingIn>
authentication/login-callback <CompletingLoggingIn>
authentication/login-failed <LogInFailed>
authentication/logout <LogOut>
authentication/logout-callback <CompletingLogOut>
authentication/logout-failed <LogOutFailed>
authentication/logged-out <LogOutSucceeded>
authentication/profile <UserProfile>
authentication/register <Registering>

Přizpůsobení uživatele

Uživatele vázané na aplikaci je možné přizpůsobit.

Přizpůsobení uživatele pomocí deklarace datové části

V následujícím příkladu obdrží ověření uživatelé amr aplikace deklaraci identity pro každou metodu ověřování uživatele. Deklarace amr identity určuje, jak byl subjekt tokenu ověřen v deklarací datové části Platformy Microsoft Identity v1.0. Příklad používá vlastní třídu uživatelských účtů založenou na RemoteUserAccount.

Vytvořte třídu, která rozšiřuje RemoteUserAccount třídu. Následující příklad nastaví AuthenticationMethod vlastnost na pole uživatele on amrJShodnoty vlastnosti. AuthenticationMethod je automaticky vyplněna rozhraním při ověření uživatele.

using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

public class CustomUserAccount : RemoteUserAccount
{
    [JsonPropertyName("amr")]
    public string[]? AuthenticationMethod { get; set; }
}
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

public class CustomUserAccount : RemoteUserAccount
{
    [JsonPropertyName("amr")]
    public string[] AuthenticationMethod { get; set; }
}

Vytvořte továrnu, která rozšiřuje AccountClaimsPrincipalFactory<TAccount> vytváření deklarací identity z metod ověřování uživatele uložených v CustomUserAccount.AuthenticationMethod:

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

public class CustomAccountFactory 
    : AccountClaimsPrincipalFactory<CustomUserAccount>
{
    public CustomAccountFactory(NavigationManager navigation, 
        IAccessTokenProviderAccessor accessor) : base(accessor)
    {
    }

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

        if (initialUser.Identity != null && initialUser.Identity.IsAuthenticated)
        {
            var userIdentity = (ClaimsIdentity)initialUser.Identity;

            if (account.AuthenticationMethod is not null)
            {
                foreach (var value in account.AuthenticationMethod)
                {
                    userIdentity.AddClaim(new Claim("amr", value));
                }
            }
        }

        return initialUser;
    }
}
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication.Internal;

public class CustomAccountFactory 
    : AccountClaimsPrincipalFactory<CustomUserAccount>
{
    public CustomAccountFactory(NavigationManager navigation, 
        IAccessTokenProviderAccessor accessor) : base(accessor)
    {
    }

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

        if (initialUser.Identity != null && initialUser.Identity.IsAuthenticated)
        {
            var userIdentity = (ClaimsIdentity)initialUser.Identity;

            foreach (var value in account.AuthenticationMethod)
            {
                userIdentity.AddClaim(new Claim("amr", value));
            }
        }

        return initialUser;
    }
}

CustomAccountFactory Zaregistrujte používaného zprostředkovatele ověřování. Některé z následujících registrací jsou platné:

  • AddOidcAuthentication:

    using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
    
    ...
    
    builder.Services.AddOidcAuthentication<RemoteAuthenticationState, 
        CustomUserAccount>(options =>
        {
            ...
        })
        .AddAccountClaimsPrincipalFactory<RemoteAuthenticationState, 
            CustomUserAccount, CustomAccountFactory>();
    
  • AddMsalAuthentication:

    using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
    
    ...
    
    builder.Services.AddMsalAuthentication<RemoteAuthenticationState, 
        CustomUserAccount>(options =>
        {
            ...
        })
        .AddAccountClaimsPrincipalFactory<RemoteAuthenticationState, 
            CustomUserAccount, CustomAccountFactory>();
    
  • AddApiAuthorization:

    using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
    
    ...
    
    builder.Services.AddApiAuthorization<RemoteAuthenticationState, 
        CustomUserAccount>(options =>
        {
            ...
        })
        .AddAccountClaimsPrincipalFactory<RemoteAuthenticationState, 
            CustomUserAccount, CustomAccountFactory>();
    

Skupiny zabezpečení ME-ID a role s vlastní třídou uživatelského účtu

Další příklad, který funguje se skupinami zabezpečení ME-ID a Správa istrator Rolemi a vlastní třídou uživatelských účtů, najdete v tématu ASP.NET Core Blazor WebAssembly se skupinami a rolemi Microsoft Entra ID.

Předběžné nastavení s ověřováním

Předběžné nastavení obsahu, který vyžaduje ověřování a autorizaci, se v současné době nepodporuje. Po provedení pokynů v jednom z Blazor WebAssembly témat aplikace zabezpečení pomocí následujících pokynů vytvořte aplikaci, která:

  • Prerenders paths for which authorization are t required.
  • Nevytáčí předem cesty, pro které se vyžaduje autorizace.

Client U souboru projektu Program zafaktorujte běžné registrace služby do samostatné metody (například vytvořte metodu ConfigureCommonServicesClient v projektu). Mezi běžné služby patří ty, které vývojář zaregistruje pro použití v projektech klienta i serveru.

public static void ConfigureCommonServices(IServiceCollection services)
{
    services.Add...;
}

V souboru Program:

var builder = WebAssemblyHostBuilder.CreateDefault(args);
...

builder.Services.AddScoped( ... );

ConfigureCommonServices(builder.Services);

await builder.Build().RunAsync();

Server V souboru projektu Program zaregistrujte následující další služby a volejteConfigureCommonServices:

using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Server;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

...

builder.Services.AddRazorPages();
builder.Services.TryAddScoped<AuthenticationStateProvider, 
    ServerAuthenticationStateProvider>();

Client.Program.ConfigureCommonServices(services);

Server V metodě projektu Startup.ConfigureServices zaregistrujte následující další služby a volejteConfigureCommonServices:

using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Server;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

public void ConfigureServices(IServiceCollection services)
{
    ...

    services.AddRazorPages();
    services.AddScoped<AuthenticationStateProvider, 
        ServerAuthenticationStateProvider>();
    services.AddScoped<SignOutSessionStateManager>();

    Client.Program.ConfigureCommonServices(services);
}

Další informace o zprostředkovateli Blazor ověřování framework serveru (ServerAuthenticationStateProvider) najdete v tématu ASP.NET Ověřování a autorizace jádraBlazor.

Server V souboru projektu Pages/_Host.cshtml nahraďte pomocnou rutinu Component značek (<component ... />) následujícím kódem:

<div id="app">
    @if (HttpContext.Request.Path.StartsWithSegments("/authentication"))
    {
        <component type="typeof({CLIENT APP ASSEMBLY NAME}.App)" 
            render-mode="WebAssembly" />
    }
    else
    {
        <component type="typeof({CLIENT APP ASSEMBLY NAME}.App)" 
            render-mode="WebAssemblyPrerendered" />
    }
</div>

V předchozím příkladu:

  • Zástupný symbol {CLIENT APP ASSEMBLY NAME} je název sestavení klientské aplikace (například BlazorSample.Client).
  • Podmíněná kontrola /authentication segmentu cesty:
    • Vyhněte se předběžnému vyřazování (render-mode="WebAssembly") pro cesty ověřování.
    • Prerenders (render-mode="WebAssemblyPrerendered") pro cesty bez ověřování.

Možnosti hostovaných aplikací a poskytovatelů přihlášení třetích stran

Při ověřování a autorizaci hostované Blazor WebAssembly aplikace u poskytovatele třetí strany je k dispozici několik možností pro ověření uživatele. Který z nich zvolíte, závisí na vašem scénáři.

Další informace najdete v tématu věnovaném zachování dalších deklarací identity a tokenů od externích zprostředkovatelů v ASP.NET Core.

Ověřování uživatelů, aby volali pouze chráněná rozhraní API třetích stran

Ověřte uživatele pomocí toku OAuth na straně klienta vůči poskytovateli rozhraní API třetí strany:

builder.services.AddOidcAuthentication(options => { ... });

V tomto scénáři:

  • Server, který je hostitelem aplikace, nehraje roli.
  • Rozhraní API na serveru nemohou být chráněna.
  • Aplikace může volat pouze chráněná rozhraní API třetích stran.

Ověřování uživatelů pomocí poskytovatele třetí strany a volání chráněných rozhraní API na hostitelském serveru a třetí straně

Konfigurujte Identity s poskytovatelem přihlášení třetí strany. Získejte tokeny potřebné pro přístup k rozhraní API třetích stran a uložte je.

Když se uživatel přihlásí, Identity shromažďuje v rámci procesu ověřování přístup a obnovovací tokeny. V tomto okamžiku je k dispozici několik přístupů k volání rozhraní API pro rozhraní API třetích stran.

Použití přístupového tokenu serveru k načtení přístupového tokenu třetí strany

Přístupový token vygenerovaný na serveru použijte k načtení přístupového tokenu třetí strany z koncového bodu rozhraní API serveru. Odtud použijte přístupový token třetí strany k volání prostředků rozhraní API třetích stran přímo z Identity klienta.

Tento přístup nedoporučujeme. Tento přístup vyžaduje zacházení s přístupovým tokenem třetí strany, jako by byl vygenerován pro veřejného klienta. Pokud jde o OAuth, veřejná aplikace nemá tajný klíč klienta, protože nemůže být důvěryhodná pro bezpečné ukládání tajných kódů a přístupový token se vytvoří pro důvěrného klienta. Důvěrný klient je klient, který má tajný klíč klienta a předpokládá se, že může bezpečně ukládat tajné kódy.

  • Přístupový token třetí strany může být udělen další obory pro provádění citlivých operací na základě skutečnosti, že třetí strana token vygenerovala pro důvěryhodnějšího klienta.
  • Podobně by obnovovací tokeny neměly být vystaveny klientovi, kterému není důvěryhodný, protože tím dáváte klientovi neomezený přístup, pokud nejsou zavedena jiná omezení.

Volání rozhraní API z klienta na serverové rozhraní API za účelem volání rozhraní API třetích stran

Volání rozhraní API z klienta na serverové rozhraní API Ze serveru načtěte přístupový token pro prostředek rozhraní API třetí strany a zadejte jakékoli volání, které je potřeba.

Tento přístup doporučujeme. I když tento přístup vyžaduje další segment směrování sítě přes server, aby volal rozhraní API třetí strany, výsledkem je nakonec bezpečnější prostředí:

  • Server může ukládat obnovovací tokeny a zajistit, aby aplikace nepřišla o přístup k prostředkům třetích stran.
  • Aplikace nemůže vytékat přístupové tokeny ze serveru, které můžou obsahovat citlivější oprávnění.

Použití koncových bodů OpenID Připojení (OIDC) v2.0

Knihovna ověřování a Blazor šablony projektů používají koncové body OpenID Připojení (OIDC) v1.0. Pokud chcete použít koncový bod verze 2.0, nakonfigurujte možnost Nosný JwtBearerOptions.Authority objekt JWT. V následujícím příkladu je ME-ID nakonfigurováno pro v2.0 připojením v2.0 segmentu k Authority vlastnosti:

using Microsoft.AspNetCore.Authentication.JwtBearer;

...

builder.Services.Configure<JwtBearerOptions>(
    JwtBearerDefaults.AuthenticationScheme, 
    options =>
    {
        options.Authority += "/v2.0";
    });

Případně můžete nastavení provést v souboru nastavení aplikace (appsettings.json):

{
  "Local": {
    "Authority": "https://login.microsoftonline.com/common/oauth2/v2.0/",
    ...
  }
}

Pokud přiřakání segmentu autoritě není vhodné pro poskytovatele OIDC aplikace, například u jiných poskytovatelů než ME-ID, nastavte Authority vlastnost přímo. Buď nastavte vlastnost v JwtBearerOptions souboru nastavení aplikace (appsettings.json) s Authority klíčem.

Seznam deklarací identity v tokenu ID se změní pro koncové body v2.0. Dokumentace Microsoftu ke změnám byla vyřazena, ale pokyny k deklaracím identity v tokenu ID jsou k dispozici v referenčních informacích k deklaracím tokenů ID.

Konfigurace a použití gRPC v součástech

Konfigurace Blazor WebAssembly aplikace tak, aby používala architekturu ASP.NET Core gRPC:

  • Povolte na serveru gRPC-Web. Další informace najdete v tématu gRPC-Web v aplikacích ASP.NET Core gRPC.
  • Zaregistrujte služby gRPC pro obslužnou rutinu zpráv aplikace. Následující příklad nakonfiguruje obslužnou rutinu autorizační zprávy aplikace tak, aby používala GreeterClient službu z kurzu gRPC ( Program soubor).

Poznámka:

Předběžné vykreslování je ve výchozím nastavení povolené ve Blazor službě Web Apps, takže musíte nejprve počítat s vykreslováním komponent ze serveru a potom z klienta. Jakýkoli předem uspořádaný stav by měl do klienta proudit, aby ho bylo možné znovu použít. Další informace naleznete v tématu Prerender ASP.NET Základní Razor komponenty.

Poznámka:

Předběžné vykreslování je ve výchozím nastavení povolené v hostovaných Blazor WebAssembly aplikacích, takže musíte nejprve počítat s vykreslováním komponent ze serveru a potom z klienta. Jakýkoli předem uspořádaný stav by měl do klienta proudit, aby ho bylo možné znovu použít. Další informace najdete v tématu Prerender a integrace komponent ASP.NET CoreRazor.

using System.Net.Http;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Grpc.Net.Client;
using Grpc.Net.Client.Web;

...

builder.Services.AddScoped(sp =>
{
    var baseAddressMessageHandler = 
        sp.GetRequiredService<BaseAddressAuthorizationMessageHandler>();
    baseAddressMessageHandler.InnerHandler = new HttpClientHandler();
    var grpcWebHandler = 
        new GrpcWebHandler(GrpcWebMode.GrpcWeb, baseAddressMessageHandler);
    var channel = GrpcChannel.ForAddress(builder.HostEnvironment.BaseAddress, 
        new GrpcChannelOptions { HttpHandler = grpcWebHandler });

    return new Greeter.GreeterClient(channel);
});

Komponenta v klientské aplikaci může provádět volání gRPC pomocí klienta gRPC (Grpc.razor):

@page "/grpc"
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]
@inject Greeter.GreeterClient GreeterClient

<h1>Invoke gRPC service</h1>

<p>
    <input @bind="name" placeholder="Type your name" />
    <button @onclick="GetGreeting" class="btn btn-primary">Call gRPC service</button>
</p>

Server response: <strong>@serverResponse</strong>

@code {
    private string name = "Bert";
    private string? serverResponse;

    private async Task GetGreeting()
    {
        try
        {
            var request = new HelloRequest { Name = name };
            var reply = await GreeterClient.SayHelloAsync(request);
            serverResponse = reply.Message;
        }
        catch (Grpc.Core.RpcException ex)
            when (ex.Status.DebugException is 
                AccessTokenNotAvailableException tokenEx)
        {
            tokenEx.Redirect();
        }
    }
}

Pokud chcete použít Status.DebugException vlastnost, použijte Grpc.Net.Client verzi 2.30.0 nebo novější.

Další informace najdete v tématu gRPC-Web v aplikacích ASP.NET Core gRPC.

AuthenticationService Nahrazení implementace

Následující pododdíly vysvětlují, jak nahradit:

  • Jakákoli implementace JavaScriptu AuthenticationService
  • Microsoft Authentication Library pro JavaScript (MSAL.js).

Nahrazení jakékoli implementace JavaScriptu AuthenticationService

Vytvořte javascriptovou knihovnu pro zpracování vlastních podrobností ověřování.

Upozorňující

Pokyny v této části jsou podrobností implementace výchozího RemoteAuthenticationService<TRemoteAuthenticationState,TAccount,TProviderOptions>. Kód TypeScriptu v této části se vztahuje konkrétně na ASP.NET Core v .NET 7 a v nadcházejících verzích ASP.NET Core se může změnit bez předchozího upozornění.

// .NET makes calls to an AuthenticationService object in the Window.
declare global {
  interface Window { AuthenticationService: AuthenticationService }
}

export interface AuthenticationService {
  // Init is called to initialize the AuthenticationService.
  public static init(settings: UserManagerSettings & AuthorizeServiceSettings, logger: any) : Promise<void>;

  // Gets the currently authenticated user.
  public static getUser() : Promise<{[key: string] : string }>;

  // Tries to get an access token silently.
  public static getAccessToken(options: AccessTokenRequestOptions) : Promise<AccessTokenResult>;

  // Tries to sign in the user or get an access token interactively.
  public static signIn(context: AuthenticationContext) : Promise<AuthenticationResult>;

  // Handles the sign-in process when a redirect is used.
  public static async completeSignIn(url: string) : Promise<AuthenticationResult>;

  // Signs the user out.
  public static signOut(context: AuthenticationContext) : Promise<AuthenticationResult>;

  // Handles the signout callback when a redirect is used.
  public static async completeSignOut(url: string) : Promise<AuthenticationResult>;
}

// The rest of these interfaces match their C# definitions.

export interface AccessTokenRequestOptions {
  scopes: string[];
  returnUrl: string;
}

export interface AccessTokenResult {
  status: AccessTokenResultStatus;
  token?: AccessToken;
}

export interface AccessToken {
  value: string;
  expires: Date;
  grantedScopes: string[];
}

export enum AccessTokenResultStatus {
  Success = 'Success',
  RequiresRedirect = 'RequiresRedirect'
}

export enum AuthenticationResultStatus {
  Redirect = 'Redirect',
  Success = 'Success',
  Failure = 'Failure',
  OperationCompleted = 'OperationCompleted'
};

export interface AuthenticationResult {
  status: AuthenticationResultStatus;
  state?: unknown;
  message?: string;
}

export interface AuthenticationContext {
  state?: unknown;
  interactiveRequest: InteractiveAuthenticationRequest;
}

export interface InteractiveAuthenticationRequest {
  scopes?: string[];
  additionalRequestParameters?: { [key: string]: any };
};

Knihovnu můžete importovat odebráním původní <script> značky a přidáním <script> značky, která načte vlastní knihovnu. Následující příklad ukazuje nahrazení výchozí <script> značky za značku, která načte knihovnu wwwroot/js pojmenovanou CustomAuthenticationService.js ze složky.

Blazor Před wwwroot/index.html skriptem (_framework/blazor.webassembly.js) uvnitř koncové </body> značky:

- <script src="_content/Microsoft.Authentication.WebAssembly.Msal/AuthenticationService.js"></script>
+ <script src="js/CustomAuthenticationService.js"></script>

Další informace najdete AuthenticationService.ts v dotnet/aspnetcore úložišti GitHub.

Poznámka:

Odkazy na dokumentaci k referenčnímu zdroji .NET obvykle načítají výchozí větev úložiště, která představuje aktuální vývoj pro příští verzi .NET. Pokud chcete vybrat značku pro konkrétní verzi, použijte rozevírací seznam pro přepnutí větví nebo značek. Další informace najdete v tématu Jak vybrat značku verze zdrojového kódu ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Nahrazení knihovny Microsoft Authentication Library pro JavaScript (MSAL.js)

Pokud aplikace vyžaduje vlastní verzi knihovny Microsoft Authentication Library pro JavaScript (MSAL.js), proveďte následující kroky:

  1. Ověřte, že systém obsahuje nejnovější sadu .NET SDK pro vývojáře, nebo získejte a nainstalujte nejnovější sadu SDK pro vývojáře ze sady .NET Core SDK: Instalační programy a binární soubory. Pro tento scénář není nutná konfigurace interních informačních kanálů NuGet.
  2. Nastavte úložiště GitHub pro dotnet/aspnetcore vývoj podle dokumentace na webu Build ASP.NET Core ze zdroje. Vytvořte fork a naklonujte nebo stáhněte archiv ZIP úložiště dotnet/aspnetcoreGitHub.
  3. src/Components/WebAssembly/Authentication.Msal/src/Interop/package.json Otevřete soubor a nastavte požadovanou verzi @azure/msal-browsersouboru . Seznam vydaných verzí najdete na @azure/msal-browser webu npm a vyberte kartu Verze .
  4. Authentication.Msal Sestavte projekt ve src/Components/WebAssembly/Authentication.Msal/src složce pomocí yarn build příkazu v příkazovém prostředí.
  5. Pokud aplikace používá komprimované prostředky (Brotli/Gzip), komprimujte Interop/dist/Release/AuthenticationService.js soubor.
  6. AuthenticationService.js Zkopírujte soubor a komprimované verze.br/.gz () souboru (pokud se vytvoří) ze Interop/dist/Release složky do publish/wwwroot/_content/Microsoft.Authentication.WebAssembly.Msal složky v publikovaných prostředcích aplikace.

Předání vlastních možností zprostředkovatele

Definujte třídu pro předávání dat do podkladové javascriptové knihovny.

Důležité

Struktura třídy musí odpovídat tomu, co knihovna očekává, když JSon je serializován s System.Text.Json.

Následující příklad ukazuje ProviderOptions třídu s JsonPropertyName atributy, které odpovídají očekávání hypotetické knihovny vlastních poskytovatelů:

public class ProviderOptions
{
    public string? Authority { get; set; }
    public string? MetadataUrl { get; set; }

    [JsonPropertyName("client_id")]
    public string? ClientId { get; set; }

    public IList<string> DefaultScopes { get; } = 
        new List<string> { "openid", "profile" };

    [JsonPropertyName("redirect_uri")]
    public string? RedirectUri { get; set; }

    [JsonPropertyName("post_logout_redirect_uri")]
    public string? PostLogoutRedirectUri { get; set; }

    [JsonPropertyName("response_type")]
    public string? ResponseType { get; set; }

    [JsonPropertyName("response_mode")]
    public string? ResponseMode { get; set; }
}
public class ProviderOptions
{
    public string Authority { get; set; }
    public string MetadataUrl { get; set; }

    [JsonPropertyName("client_id")]
    public string ClientId { get; set; }

    public IList<string> DefaultScopes { get; } = 
        new List<string> { "openid", "profile" };

    [JsonPropertyName("redirect_uri")]
    public string RedirectUri { get; set; }

    [JsonPropertyName("post_logout_redirect_uri")]
    public string PostLogoutRedirectUri { get; set; }

    [JsonPropertyName("response_type")]
    public string ResponseType { get; set; }

    [JsonPropertyName("response_mode")]
    public string ResponseMode { get; set; }
}

Zaregistrujte možnosti poskytovatele v systému DI a nakonfigurujte příslušné hodnoty:

builder.Services.AddRemoteAuthentication<RemoteAuthenticationState, RemoteUserAccount,
    ProviderOptions>(options => {
        options.Authority = "...";
        options.MetadataUrl = "...";
        options.ClientId = "...";
        options.DefaultScopes = new List<string> { "openid", "profile", "myApi" };
        options.RedirectUri = "https://localhost:5001/authentication/login-callback";
        options.PostLogoutRedirectUri = "https://localhost:5001/authentication/logout-callback";
        options.ResponseType = "...";
        options.ResponseMode = "...";
    });

Předchozí příklad nastaví identifikátory URI přesměrování s běžnými řetězcovými literály. K dispozici jsou následující alternativy:

  • TryCreate using IWebAssemblyHostEnvironment.BaseAddress:

    Uri.TryCreate(
        $"{builder.HostEnvironment.BaseAddress}authentication/login-callback", 
        UriKind.Absolute, out var redirectUri);
    options.RedirectUri = redirectUri;
    
  • Konfigurace tvůrce hostitelů:

    options.RedirectUri = builder.Configuration["RedirectUri"];
    

    wwwroot/appsettings.json:

    {
      "RedirectUri": "https://localhost:5001/authentication/login-callback"
    }
    

Další materiály