Sdílet prostřednictvím


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

Note

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

Warning

Tato verze ASP.NET Core se už nepodporuje. Další informace najdete v zásadách podpory .NET a .NET Core. Aktuální verzi tohoto článku naleznete ve verzi .NET 9.

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

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

AuthorizationMessageHandler je to DelegatingHandler, který slouží ke zpracování přístupových tokenů. 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 URI odchozích požadavků nejsou v základním identifikátoru URI aplikace, použijte vlastní AuthorizationMessageHandler třídu (doporučeno) nebo nakonfigurujte AuthorizationMessageHandler.

Note

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.

Note

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 Blazorřešení založeného na šabloně Blazor WebAssembly projektu jsou URI požadavků v rámci základního URI aplikace. Proto je IWebAssemblyHostEnvironment.BaseAddress (new Uri(builder.HostEnvironment.BaseAddress)) přiřazen k HttpClient.BaseAddress v aplikaci vygenerované ze šablony projektu.

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í.
  • loginHint je nastavena na peter@contoso.com: Předvyplní pole uživatelské jméno/e-mailová adresa na přihlašovací stránce pro uživatele na peter@contoso.com. Aplikace tento parametr často používají při opětovném ověřování, přičemž uživatelské jméno již bylo extrahováno z předchozího přihlášení pomocí nároku preferred_username.

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 AccessTokenNotAvailableException, 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á data JSON 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í.
  • loginHint je nastavena na peter@contoso.com: Předvyplní pole uživatelské jméno/e-mailová adresa na přihlašovací stránce pro uživatele na peter@contoso.com. Aplikace tento parametr často používají při opětovném ověřování, přičemž uživatelské jméno již bylo extrahováno z předchozího přihlášení pomocí nároku preferred_username.
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 se při použití IAccessTokenProvidernepodaří získat token, spravujte další parametry pro žádost o nový přístupový token poskytovatele identity jednou nebo vícekrát na 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í.
  • loginHint je nastavena na peter@contoso.com: Předvyplní pole uživatelské jméno/e-mailová adresa na přihlašovací stránce pro uživatele na peter@contoso.com. Aplikace tento parametr často používají při opětovném ověřování, přičemž uživatelské jméno již bylo extrahováno z předchozího přihlášení pomocí nároku preferred_username.
var tokenResult = await TokenProvider.RequestAccessToken(
    new AccessTokenRequestOptions
    {
        Scopes = [ ... ]
    });

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

    Navigation.NavigateToLogin(accessTokenResult.InteractiveRequestUrl, 
        accessTokenResult.InteractionOptions);
}
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ískat přihlašovací cestu 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 pro 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: [ "https://api.contoso.com/v1.0" ],
            scopes: [ "example.read", "example.write" ]);
    }
}

Note

V této části se použije předchozí obslužný program zprávy při vytvoření nakonfigurovaného HttpClient z vloženého IHttpClientFactory. Pokud nepoužíváte IHttpClientFactoryinstanci, musíte vytvořit HttpClientHandler instanci a přiřadit ji k objektu AuthorizationMessageHandler's DelegatingHandler.InnerHandler:

InnerHandler = new HttpClientHandler();

Nemusíte provádět předchozí InnerHandler přiřazení, pokud použijete IHttpClientFactory, protože jak ExampleAPIMethod volání demonstruje později v této části.

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.

V souboru Program je CustomAuthorizationMessageHandler registrované jako přechodná služba a konfigurováno jako DelegatingHandler pro odchozí instance HttpResponseMessage 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.

Note

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

Note

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:

Pro hostované Blazor řešení založené na Blazor WebAssembly šabloně projektu je IWebAssemblyHostEnvironment.BaseAddress (new Uri(builder.HostEnvironment.BaseAddress)) přiřazeno k HttpClient.BaseAddress.

Nakonfigurované HttpClient se používá k provádění autorizovaných požadavků pomocí try-catch vzoru. Když je klient vytvořen pomocí CreateClient (Microsoft.Extensions.Http balíčku), jsou poskytnuty instance HttpClient, 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, jako je tomu v následujícím příkladu (ExampleAPIMethod), zkombinuje se s BaseAddress, když klientská aplikace provede požadavek.

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

Konfigurovat AuthorizationMessageHandler

Pomocí metody AuthorizationMessageHandler lze ConfigureHandler nakonfigurovat s autorizovanými adresami URL, rozsahy a návratovou adresou URL. 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 URI požadavku relativní, zkombinuje se s identifikátorem BaseAddress.

V následujícím příkladu AuthorizationMessageHandlerHttpClient nakonfiguruje souborProgram:

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

...

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

V předchozím kódu:

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

Psaný na stroji HttpClient

Lze definovat typového klienta, který zpracovává všechny záležitosti týkající se HTTP a získávání tokenů v rámci jedné třídy.

WeatherForecastClient.cs:

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

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

    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ý {PACKAGE ID/ASSEMBLY NAME} symbol je ID balíčku projektu (<PackageId> v souboru projektu) pro knihovnu nebo 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.

Note

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

Pro hostované Blazor řešení založené na Blazor WebAssembly šabloně projektu je IWebAssemblyHostEnvironment.BaseAddress (new Uri(builder.HostEnvironment.BaseAddress)) přiřazeno k HttpClient.BaseAddress.

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

@inject WeatherForecastClient Client

...

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

Nakonfigurujte obslužnou rutinu HttpClient

Obslužnou rutinu je možné nakonfigurovat dále s 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.

Note

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: [ "https://api.contoso.com/v1.0" ],
        scopes: [ "example.read", "example.write" ]));
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 Blazor hostované řešení založené na Blazor WebAssembly šabloně, je projektu IWebAssemblyHostEnvironment.BaseAddress 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 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.

Note

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

Pro hostované Blazor řešení založené na Blazor WebAssembly šabloně projektu je IWebAssemblyHostEnvironment.BaseAddress (new Uri(builder.HostEnvironment.BaseAddress)) přiřazeno k HttpClient.BaseAddress.

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

Komponenta vytvoří HttpClient z IHttpClientFactory (Microsoft.Extensions.Http balíčku), aby vytvářela 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");

        ...
    }
}

Note

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í IHttpClientFactory je vytvoření typové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í HttpClient vyžadován další rozsah 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 1} a {CUSTOM SCOPE 2} zástupné symboly v předchozím příkladu jsou vlastní rozsahy.

Note

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 možnost přetížení, které aplikaci umožňuje zřídit přístupový token s danou sadou rozsahů.

V komponentě Razor :

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

...

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

if (tokenResult.TryGetToken(out var token))
{
    ...
}
@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 1} a {CUSTOM SCOPE 2} zástupné symboly v předchozím příkladu jsou vlastní rozsahy.

AccessTokenResult.TryGetToken výstupy:

  • true s token k použití.
  • false pokud token není získán.

Sdílení prostředků mezi různými zdroji (CORS - Cross-Origin Resource Sharing)

Při odesílání přihlašovacích údajů (autorizační soubory cookie nebo hlavičky) 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 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 komponentě Tester 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 Connect (OIDC), stav ověřování se udržuje místně v rámci SPA a u poskytovatele identity (IP) ve formě relace, která je nastavena 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ů mohou klienti OIDC zřizovat nové tokeny, aniž by uživatel musel znovu projít autentizací, 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. Jednostránková aplikace (SPA) využívající soubory cookie také nedokáže volat serverové API, pokud je ověřování cookie odebráno.

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 do session storage. 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() => 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 ApplicationAuthenticationState z RemoteAuthenticationState. 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; }
}

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

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

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

builder.Services.AddSingleton<StateContainer>();

Přizpůsobení tras aplikací

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

Route Purpose
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 opatřeny 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 jsou požadovány zcela jiné trasy, nastavte tyto trasy, jak je popsáno výše, a vykreslujte RemoteAuthenticatorView s 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 RenderFragment. 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.

Route 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ůsobte 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. Nárok amr určuje, jakým způsobem byl subjekt tokenu ověřen pomocí nároků v Microsoft identity platform 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 uživatelovo pole hodnot vlastností amr JSON. 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; }
}

Vytvořte továrnu, která rozšiřuje AccountClaimsPrincipalFactory<TAccount> a vytváří deklarace 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(NavigationManager navigation,
    IAccessTokenProviderAccessor accessor)
    : AccountClaimsPrincipalFactory<CustomUserAccount>(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;
    }
}

Zaregistrujte CustomAccountFactory pro 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 rolemi správce ME-ID a vlastní třídou uživatelských účtů, najdete v tématu ASP.NET Core Blazor WebAssembly se skupinami a rolemi ID Microsoft Entra.

Předběžné vykreslování 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á:

  • Předem vykresluje cesty, pro které není vyžadováno povolení.
  • Nepředvykresluje trasy, pro které je vyžadována autorizace.

U souboru projektu Client souboru Program shrňte běžné registrace služeb do samostatné metody (například vytvořte metodu ConfigureCommonServices v projektu Client). 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 ověřování serveru frameworku (Blazor, ServerAuthenticationStateProvider) najdete v tématu Autentizace a autorizace jádra ASP.NET Blazor.

V souboru projektu ServerPages/_Host.cshtml, nahraďte pomocníka značek Component(<component ... />) následujícím:

<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ý {CLIENT APP ASSEMBLY NAME} symbol je název sestavení klientské aplikace (například BlazorSample.Client).
  • Podmíněná kontrola segmentu cesty /authentication:
    • Předběžné vykreslování (render-mode="WebAssembly") se vynechává pro cesty ověřování.
    • Prerenders (render-mode="WebAssemblyPrerendered") pro trasy 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ěřte uživatele, aby mohli volat pouze chráněná 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 způsobů volání 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

Zavolejte API z klienta na API serveru. 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ší síťový skok přes server, aby volal rozhraní API třetí strany, nakonec je výsledkem 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 Connect (OIDC) v2.0

Knihovna ověřování a Blazor šablony projektů používají koncové body OpenID Connect (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, nebo v souboru nastavení aplikace (appsettings.json) s klíčem Authority.

Seznam nároků v ID tokenu 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.
  • Registrujte služby gRPC pro handler zpráv aplikace. Následující příklad nakonfiguruje obslužnou rutinu autorizační zprávy aplikace tak, aby používala službu z GreeterClient ( soubor).

Note

Předběžné vykreslování je ve výchozím nastavení povolené ve Blazor Web Apps, takže musíte nejprve zohlednit vykreslování 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 ASP.NET Blazor trvalost stavu předsekutného jádra.

Note

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 Integrace komponent ASP.NET Core Razor s MVC nebo Razor Stránkami v hostovaných Blazor WebAssembly řešeních.

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í.

Warning

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 CustomAuthenticationService.js pojmenovanou wwwroot/js ze složky.

wwwroot/index.html Před Blazor 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.

Note

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řepínání 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 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 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 vývoj podle dokumentu dotnet/aspnetcore. Vytvořte fork a naklonujte nebo stáhněte archiv ZIP úložiště dotnet/aspnetcoreGitHub.
  3. Otevřete src/Components/WebAssembly/Authentication.Msal/src/Interop/package.json soubor a nastavte požadovanou verzi @azure/msal-browser. 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. Zkopírujte soubor AuthenticationService.js a jeho komprimované verze (.br/.gz) (pokud se vytvoří) ze složky Interop/dist/Release do složky publish/wwwroot/_content/Microsoft.Authentication.WebAssembly.Msal v publikovaných prostředcích aplikace.

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

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

Important

Struktura třídy musí odpovídat tomu, co knihovna očekává, když je JSON 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; set; } = [ "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.ProviderOptions.Authority = "...";
        options.ProviderOptions.MetadataUrl = "...";
        options.ProviderOptions.ClientId = "...";
        options.ProviderOptions.DefaultScopes = [ "openid", "profile", "myApi" ];
        options.ProviderOptions.RedirectUri = "https://localhost:5001/authentication/login-callback";
        options.ProviderOptions.PostLogoutRedirectUri = "https://localhost:5001/authentication/logout-callback";
        options.ProviderOptions.ResponseType = "...";
        options.ProviderOptions.ResponseMode = "...";
    });

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

  • TryCreate použití IWebAssemblyHostEnvironment.BaseAddress:

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

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

    wwwroot/appsettings.json:

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

Dodatečné zdroje