Dela via


ASP.NET Core på serversidan och Blazor Web App ytterligare säkerhetsscenarier

Notera

Det här är inte den senaste versionen av den här artikeln. Den aktuella versionen finns i den .NET 9-versionen av den här artikeln.

Varning

Den här versionen av ASP.NET Core stöds inte längre. Mer information finns i .NET och .NET Core Support Policy. Den aktuella versionen finns i den .NET 9-versionen av den här artikeln.

Viktig

Den här informationen gäller en förhandsversionsprodukt som kan ändras avsevärt innan den släpps kommersiellt. Microsoft lämnar inga garantier, uttryckliga eller underförstådda, med avseende på den information som tillhandahålls här.

Den aktuella versionen finns i den .NET 9-versionen av den här artikeln.

I den här artikeln beskrivs hur du konfigurerar Blazor på serversidan för ytterligare säkerhetsscenarier, inklusive hur du skickar token till en Blazor app.

Notera

Kodexemplen i den här artikeln använder nullbara referenstyper (NRT) och .NET-kompilatorn null-state static analysis, som stöds i ASP.NET Core i .NET 6 eller senare. När du riktar in dig på .NET 5 eller tidigare tar du bort null-typbeteckningen (?) från typerna string?, TodoItem[]?, WeatherForecast[]?och IEnumerable<GitHubBranch>? i artikelns exempel.

Skicka tokens till en Blazor-app på server-sidan

Det här avsnittet gäller för Blazor Web Apps. För att se Blazor Server, visa .NET 7-versionen av den här avsnittet av artikeln.

Om du bara vill använda åtkomsttoken för att göra webb-API-anrop från en Blazor Web App med en namngiven HTTP-klient läser du avsnittet Använda en tokenhanterare för webb-API-anrop , som förklarar hur du använder en DelegatingHandler implementering för att koppla en användares åtkomsttoken till utgående begäranden. Följande vägledning i det här avsnittet gäller för utvecklare som behöver åtkomsttoken, uppdateringstoken och andra autentiseringsegenskaper på serversidan för andra ändamål.

Om du vill spara token och andra autentiseringsegenskaper för användning på serversidan i Blazor Web Apps rekommenderar vi att du använder IHttpContextAccessor/HttpContext (IHttpContextAccessor, HttpContext). Läsning av token från HttpContext, inklusive som en kaskaderande parameter, stöds för att hämta token med IHttpContextAccessor för användning under interaktiv serverrendering om token hämtas under statisk återgivning på serversidan (statisk SSR) eller förrendering. Token uppdateras dock inte om användaren autentiserar efter att kretsen har upprättats, eftersom HttpContext fångas vid starten av SignalR-anslutningen. Dessutom innebär användningen av AsyncLocal<T> av IHttpContextAccessor att du måste vara noga med att inte förlora körningskontexten innan du läser HttpContext. Mer information finns i IHttpContextAccessor/HttpContext i ASP.NET Core Blazor-appar.

I en tjänstklass får du åtkomst till medlemmarna i namnområdet Microsoft.AspNetCore.Authentication för att visa metoden på GetTokenAsyncHttpContext. En alternativ metod, som kommenteras ut i följande exempel, är att anropa AuthenticateAsyncHttpContext. För den returnerade AuthenticateResult.Propertiesanropar du GetTokenValue.

using Microsoft.AspNetCore.Authentication;

public class AuthenticationProcessor(IHttpContextAccessor httpContextAccessor)
{
    public async Task<string?> GetAccessToken()
    {
        if (httpContextAccessor.HttpContext is null)
        {
            throw new Exception("HttpContext not available");
        }

        // Approach 1: Call 'GetTokenAsync'
        var accessToken = await httpContextAccessor.HttpContext
            .GetTokenAsync("access_token");

        // Approach 2: Authenticate the user and call 'GetTokenValue'
        /*
        var authResult = await httpContextAccessor.HttpContext.AuthenticateAsync();
        var accessToken = authResult?.Properties?.GetTokenValue("access_token");
        */

        return accessToken;
    }
}

Tjänsten är registrerad i serverprojektets Program fil:

builder.Services.AddScoped<AuthenticationProcessor>();

AuthenticationProcessor kan injiceras i tjänster på serversidan, till exempel i en DelegatingHandler för en förkonfigurerad HttpClient. Följande exempel är endast i demonstrationssyfte eller om du behöver utföra särskild bearbetning i tjänsten eftersom du bara kan mata in AuthenticationProcessor och hämta token direkt för att anropa externa webb-API:er (mer information om hur du använder IHttpContextAccessor direkt för att anropa webb-API:er finns i IHttpContextAccessor avsnittet Använda en tokenhanterare för webb-API-anrop).

using System.Net.Http.Headers;

public class TokenHandler(AuthenticationProcessor authProcessor) : 
    DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var accessToken = authProcessor.GetAccessToken();

        request.Headers.Authorization =
            new AuthenticationHeaderValue("Bearer", accessToken);

        return await base.SendAsync(request, cancellationToken);
    }
}

Tokenhanteraren är registrerad och fungerar som delegeringshanterare för en namngiven Program HTTP-klient i filen:

builder.Services.AddHttpContextAccessor();

builder.Services.AddScoped<TokenHandler>();

builder.Services.AddHttpClient("ExternalApi",
      client => client.BaseAddress = new Uri(builder.Configuration["ExternalApiUri"] ?? 
          throw new Exception("Missing base address!")))
      .AddHttpMessageHandler<TokenHandler>();

Försiktighet

Se till att token aldrig överförs och hanteras av klienten (projektet .Client), till exempel i en komponent som implementerar interaktiv automatisk rendering och renderas på klienten eller av en tjänst på klientsidan. Be alltid klienten anropa servern (projektet) för att bearbeta begäranden med token. Token och andra autentiseringsdata bör aldrig lämna servern.

Interaktiva automatiska komponenter finns i ASP.NET Core-autentisering Blazor och -auktorisering, som visar hur du lämnar åtkomsttoken och andra autentiseringsegenskaper på servern. Överväg också att använda Backend-for-Frontend-mönstret (BFF), som antar en liknande anropsstruktur och beskrivs i Säkra en ASP.NET Core Blazor Web App med OpenID Connect (OIDC) för OIDC-leverantörer och Säkra en ASP.NET Core Blazor Web App med Microsoft Entra ID för Microsoft Identity Web med Entra.

Använda en tokenhanterare för webb-API-anrop

Följande metod syftar till att koppla en användares åtkomsttoken till utgående begäranden, särskilt för att göra webb-API-anrop till externa webb-API-appar. Metoden visas för en Blazor Web App som använder global interaktiv serverrendering, men samma allmänna metod gäller för Blazor Web Apps som använder det globala läget för interaktiv automatisk återgivning. Det viktiga begreppet är att åtkomst till HttpContext genom att använda IHttpContextAccessor endast utförs på servern.

En demonstration av vägledningen i det här avsnittet finns i exempelapparna BlazorWebAppOidc och BlazorWebAppOidcServer (.NET 8 eller senare) i Blazor GitHub-exempellagringsplatsen. Exemplen använder ett globalt interaktivt återgivningsläge och OIDC-autentisering med Microsoft Entra utan att använda Entra-specifika paket. Exemplen visar hur du skickar en JWT-åtkomsttoken för att anropa ett säkert webb-API.

Microsofts identitetsplattform med Microsoft Identity webbpaket för Microsoft Entra ID tillhandahåller ett API för att anropa webb-API:er från Blazor Web App med automatisk tokenhantering och förnyelse. Mer information finns i Skydda en ASP.NET Core Blazor Web App med Microsoft Entra-ID och exempelapparna BlazorWebAppEntraBlazorWebAppEntraBff (.NET 9 eller senare) på Blazor GitHub-exempellagringsplatsen.

Underklass DelegatingHandler för att koppla en användares åtkomsttoken till utgående begäranden. Tokenhanteraren körs bara på servern, så att använda HttpContext är säkert.

TokenHandler.cs:

using System.Net.Http.Headers;
using Microsoft.AspNetCore.Authentication;

public class TokenHandler(IHttpContextAccessor httpContextAccessor) : 
    DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        if (httpContextAccessor.HttpContext is null)
        {
            throw new Exception("HttpContext not available");
        }

        var accessToken = await httpContextAccessor.HttpContext.GetTokenAsync("access_token");

        if (accessToken is null)
        {
            throw new Exception("No access token");
        }

        request.Headers.Authorization =
            new AuthenticationHeaderValue("Bearer", accessToken);

        return await base.SendAsync(request, cancellationToken);
    }
}

Notera

Information om hur du får åtkomst till en AuthenticationStateProvider från en DelegatingHandlerfinns i avsnittet Åtkomst AuthenticationStateProvider i mellanprogram för utgående begäran .

I projektets Program fil registreras tokenhanteraren (TokenHandler) som en begränsad tjänst och anges som en namngiven HTTP-klients meddelandehanterare med AddHttpMessageHandler.

I följande exempel {HTTP CLIENT NAME} är platshållaren namnet på HttpClient, och {BASE ADDRESS} platshållaren är webb-API:ets basadress-URI. Mer information om AddHttpContextAccessorfinns i IHttpContextAccessor/HttpContext i ASP.NET Core-apparBlazor.

I Program.cs:

builder.Services.AddHttpContextAccessor();

builder.Services.AddScoped<TokenHandler>();

builder.Services.AddHttpClient("{HTTP CLIENT NAME}",
      client => client.BaseAddress = new Uri("{BASE ADDRESS}"))
      .AddHttpMessageHandler<TokenHandler>();

Exempel:

builder.Services.AddScoped<TokenHandler>();

builder.Services.AddHttpClient("ExternalApi",
      client => client.BaseAddress = new Uri("https://localhost:7277"))
      .AddHttpMessageHandler<TokenHandler>();

Du kan ange HTTP-klientens basadress från konfigurationen med builder.Configuration["{CONFIGURATION KEY}"], där {CONFIGURATION KEY}-platshållaren är konfigurationsnyckeln.

new Uri(builder.Configuration["ExternalApiUri"] ?? throw new IOException("No URI!"))

Ange appsettings.json där du specificerar ExternalApiUri. I följande exempel anges värdet till localhost-adressen för det externa webb-API:et till https://localhost:7277:

"ExternalApiUri": "https://localhost:7277"

Nu kan en HttpClient som skapats av en komponent göra säkra webb-API-begäranden. I följande exempel {REQUEST URI} är den relativa begärande-URI:n och {HTTP CLIENT NAME} platshållaren är namnet på HttpClient:

using var request = new HttpRequestMessage(HttpMethod.Get, "{REQUEST URI}");
var client = ClientFactory.CreateClient("{HTTP CLIENT NAME}");
using var response = await client.SendAsync(request);

Exempel:

using var request = new HttpRequestMessage(HttpMethod.Get, "/weather-forecast");
var client = ClientFactory.CreateClient("ExternalApi");
using var response = await client.SendAsync(request);

Ytterligare funktioner planeras för Blazor, som spåras av Access AuthenticationStateProvider i middleware för utgående begäran (dotnet/aspnetcore #52379). Problem med att tillhandahålla åtkomsttoken till HttpClient i interaktivt serverläge (dotnet/aspnetcore #52390) är ett stängt problem som innehåller användbara diskussioner och potentiella lösningsstrategier för avancerade användningsfall.

Tokener som är tillgängliga utanför Razor-komponenter i en serverside Blazor-app kan skickas till komponenter med den metod som beskrivs i det här avsnittet. Exemplet i det här avsnittet fokuserar på att skicka åtkomsttoken, uppdateringstoken och anti-request forgery (XSRF) token till Blazor-appen, men metoden är tillämplig för andra HTTP-kontexttillstånd.

Notera

Att skicka XSRF-token till Razor komponenter är användbart i scenarier där komponenterna POST till Identity eller andra slutpunkter som kräver validering. Om din app bara kräver åtkomst- och uppdateringstoken kan du ta bort XSRF-tokenkoden från följande exempel.

Autentisera appen på samma sätt som med en vanlig Razor Pages- eller MVC-app. Tilldela och spara token till autentiseringen cookie.

I filen Program:

using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;

...

builder.Services.Configure<OpenIdConnectOptions>(
    OpenIdConnectDefaults.AuthenticationScheme, options =>
{
    options.ResponseType = OpenIdConnectResponseType.Code;
    options.SaveTokens = true;
    options.Scope.Add(OpenIdConnectScope.OfflineAccess);
});

I Startup.cs:

using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;

...

services.Configure<OpenIdConnectOptions>(
    OpenIdConnectDefaults.AuthenticationScheme, options =>
{
    options.ResponseType = OpenIdConnectResponseType.Code;
    options.SaveTokens = true;
    options.Scope.Add(OpenIdConnectScope.OfflineAccess);
});

I Startup.cs:

using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;

...

services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
    options.ResponseType = OpenIdConnectResponseType.Code;
    options.SaveTokens = true;
    options.Scope.Add(OpenIdConnectScope.OfflineAccess);
});

Du kan också lägga till ytterligare omfång med options.Scope.Add("{SCOPE}");, där platshållaren {SCOPE} är det ytterligare omfång som ska läggas till.

Definiera en begränsad tokenprovidertjänst som kan användas i Blazor-appen för att matcha token från beroendeinmatning (DI).

TokenProvider.cs:

public class TokenProvider
{
    public string? AccessToken { get; set; }
    public string? RefreshToken { get; set; }
    public string? XsrfToken { get; set; }
}

I filen Program lägger du till tjänster för:

  • IHttpClientFactory: Används i en WeatherForecastService-klass som hämtar väderdata från ett server-API med en åtkomsttoken.
  • TokenProvider: Innehåller åtkomst- och uppdateringstoken.
builder.Services.AddHttpClient();
builder.Services.AddScoped<TokenProvider>();

I Startup.ConfigureServices av Startup.cslägger du till tjänster för:

  • IHttpClientFactory: Används i en WeatherForecastService-klass som hämtar väderdata från ett server-API med en åtkomsttoken.
  • TokenProvider: Innehåller åtkomst- och uppdateringstoken.
services.AddHttpClient();
services.AddScoped<TokenProvider>();

Definiera en klass som ska skickas i det inledande apptillståndet med åtkomst- och uppdateringstoken.

InitialApplicationState.cs:

public class InitialApplicationState
{
    public string? AccessToken { get; set; }
    public string? RefreshToken { get; set; }
    public string? XsrfToken { get; set; }
}

I filen Pages/_Host.cshtml skapar en instans av InitialApplicationState och skickar denna som en parameter till appen.

I filen Pages/_Layout.cshtml skapar en instans av InitialApplicationState och skickar denna som en parameter till appen.

I filen Pages/_Host.cshtml skapar en instans av InitialApplicationState och skickar denna som en parameter till appen.

@using Microsoft.AspNetCore.Authentication
@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Xsrf

...

@{
    var tokens = new InitialApplicationState
    {
        AccessToken = await HttpContext.GetTokenAsync("access_token"),
        RefreshToken = await HttpContext.GetTokenAsync("refresh_token"),
        XsrfToken = Xsrf.GetAndStoreTokens(HttpContext).RequestToken
    };
}

<component ... param-InitialState="tokens" ... />

I komponenten App (App.razor) löser du tjänsten och initierar den med data från parametern:

@inject TokenProvider TokenProvider

...

@code {
    [Parameter]
    public InitialApplicationState? InitialState { get; set; }

    protected override Task OnInitializedAsync()
    {
        TokenProvider.AccessToken = InitialState?.AccessToken;
        TokenProvider.RefreshToken = InitialState?.RefreshToken;
        TokenProvider.XsrfToken = InitialState?.XsrfToken;

        return base.OnInitializedAsync();
    }
}

Notera

Ett alternativ till att tilldela det inledande tillståndet till TokenProvider i föregående exempel är att kopiera data till en begränsad tjänst inom OnInitializedAsync för användning i appen.

Lägg till en paketreferens till appen för Microsoft.AspNet.WebApi.Client NuGet-paketet.

Notera

Mer information om hur du lägger till paket i .NET-appar finns i artiklarna under Installera och hantera paketArbetsflöde för paketförbrukning (NuGet-dokumentation). Bekräfta rätt paketversioner på NuGet.org.

I tjänsten som gör en säker API-begäran matar du in tokenprovidern och hämtar token för API-begäran:

WeatherForecastService.cs:

using System;
using System.Net.Http;
using System.Threading.Tasks;

public class WeatherForecastService
{
    private readonly HttpClient http;
    private readonly TokenProvider tokenProvider;

    public WeatherForecastService(IHttpClientFactory clientFactory, 
        TokenProvider tokenProvider)
    {
        http = clientFactory.CreateClient();
        this.tokenProvider = tokenProvider;
    }

    public async Task<WeatherForecast[]> GetForecastAsync()
    {
        var token = tokenProvider.AccessToken;
        using var request = new HttpRequestMessage(HttpMethod.Get, 
            "https://localhost:5003/WeatherForecast");
        request.Headers.Add("Authorization", $"Bearer {token}");
        using var response = await http.SendAsync(request);
        response.EnsureSuccessStatusCode();

        return await response.Content.ReadFromJsonAsync<WeatherForecast[]>() ?? 
            Array.Empty<WeatherForecast>();
    }
}

För en XSRF-token som skickas till en komponent matar du in TokenProvider och lägger till XSRF-token i POST-begäran. I följande exempel läggs token till i slutpunkten POST för utloggning. Scenariot för följande exempel är att utloggningsslutpunkten (Areas/Identity/Pages/Account/Logout.cshtml, genererats inuti appen) inte anger en IgnoreAntiforgeryTokenAttribute (@attribute [IgnoreAntiforgeryToken]) eftersom den utför en åtgärd förutom en vanlig utloggning som måste skyddas. Slutpunkten kräver en giltig XSRF-token för att bearbeta begäran.

I en komponent som visar en utloggningsknapp för behöriga användare:

@inject TokenProvider TokenProvider

...

<AuthorizeView>
    <Authorized>
        <form action="/Identity/Account/Logout?returnUrl=%2F" method="post">
            <button class="nav-link btn btn-link" type="submit">Logout</button>
            <input name="__RequestVerificationToken" type="hidden" 
                value="@TokenProvider.XsrfToken">
        </form>
    </Authorized>
    <NotAuthorized>
        ...
    </NotAuthorized>
</AuthorizeView>

Ange autentiseringsschemat

För en app som använder mer än ett mellanprogram för autentisering och därmed har fler än ett autentiseringsschema kan schemat som Blazor använder uttryckligen anges i slutpunktskonfigurationen för Program-filen. I följande exempel anges OIDC-schemat (OpenID Connect):

För en app som använder mer än ett autentiseringsmellanprogram och därmed har fler än ett autentiseringsschema kan det schema som Blazor använder uttryckligen anges i slutpunktskonfigurationen för Startup.cs. I följande exempel anges OIDC-schemat (OpenID Connect):

using Microsoft.AspNetCore.Authentication.OpenIdConnect;

...

app.MapRazorComponents<App>().RequireAuthorization(
    new AuthorizeAttribute
    {
        AuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme
    })
    .AddInteractiveServerRenderMode();
using Microsoft.AspNetCore.Authentication.OpenIdConnect;

...

app.MapBlazorHub().RequireAuthorization(
    new AuthorizeAttribute 
    {
        AuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme
    });

För en app som använder mer än ett autentiseringsmellanprogram och därmed har fler än ett autentiseringsschema kan det schema som Blazor använder uttryckligen anges i slutpunktskonfigurationen för Startup.Configure. I följande exempel anges Microsoft Entra ID-schemat:

endpoints.MapBlazorHub().RequireAuthorization(
    new AuthorizeAttribute 
    {
        AuthenticationSchemes = AzureADDefaults.AuthenticationScheme
    });

Använda OpenID Connect-slutpunkter (OIDC) v2.0

I versioner av ASP.NET Core före .NET 5 använder autentiseringsbiblioteket och Blazor mallarna OpenID Connect (OIDC) v1.0-slutpunkter. Om du vill använda en v2.0-slutpunkt med versioner av ASP.NET Core före .NET 5 konfigurerar du OpenIdConnectOptions.Authority alternativet i OpenIdConnectOptions:

services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, 
    options =>
    {
        options.Authority += "/v2.0";
    }

Du kan också göra inställningen i appinställningarna (appsettings.json) filen:

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

Om det inte är lämpligt att fästa ett segment på auktoriteten för appens OIDC-leverantör, till exempel med icke-ME-ID-leverantörer, anger du egenskapen Authority direkt. Ange antingen egenskapen i OpenIdConnectOptions eller i appinställningsfilen med Authority-nyckeln.

Kodändringar

  • Listan över anspråk i ID-token ändras för v2.0-slutpunkter. Microsofts dokumentation om ändringarna har dragits tillbaka, men vägledning om anspråken i en ID-token finns i -referensen för ID-tokenanspråk.

  • Eftersom resurser anges i omfångs-URI:er för v2.0-slutpunkter tar du bort egenskapsinställningen OpenIdConnectOptions.Resource i OpenIdConnectOptions:

    services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options => 
        {
            ...
            options.Resource = "...";    // REMOVE THIS LINE
            ...
        }
    

App-ID URI

  • När du använder v2.0-slutpunkter definierar API:er en App ID URI, som är avsedd att representera en unik identifierare för API:et.
  • Alla omfång inkluderar app-ID-URI:n som ett prefix och v2.0-slutpunkter genererar åtkomsttoken med app-ID-URI:n som målgrupp.
  • När du använder V2.0-slutpunkter ändras klient-ID:t som konfigurerats i server-API:et från API-program-ID :t (klient-ID) till app-ID-URI:n.

appsettings.json:

{
  "AzureAd": {
    ...
    "ClientId": "https://{TENANT}.onmicrosoft.com/{PROJECT NAME}"
    ...
  }
}

Du hittar app-ID-URI:n som ska användas i registreringsbeskrivningen för OIDC-providerappen.

Kretshanterare för att samla in användare för anpassade tjänster

Använd en CircuitHandler för att samla in en användare från AuthenticationStateProvider och ange användaren i en tjänst. Om du vill uppdatera användaren registrerar du ett återanrop till AuthenticationStateChanged och begär en Task för att hämta den nya användaren och uppdatera tjänsten. I följande exempel visas metoden.

I följande exempel:

  • OnConnectionUpAsync anropas varje gång kretsen återansluter och anger användaren för anslutningens livslängd. Endast metoden OnConnectionUpAsync krävs om du inte implementerar uppdateringar via en hanterare för autentiseringsändringar (AuthenticationChanged i följande exempel).
  • OnCircuitOpenedAsync anropas för att koppla den ändrade autentiseringshanteraren AuthenticationChangedför att uppdatera användaren.
  • Blocket catch för aktiviteten UpdateAuthentication vidtar inga åtgärder för undantag eftersom det inte finns något sätt att rapportera dem vid den här tidpunkten i programkörningen. Om ett undantag genereras från aktiviteten rapporteras undantaget någon annanstans i appen.

UserService.cs:

using System.Security.Claims;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Server.Circuits;

public class UserService
{
    private ClaimsPrincipal currentUser = new(new ClaimsIdentity());

    public ClaimsPrincipal GetUser() => currentUser;

    internal void SetUser(ClaimsPrincipal user)
    {
        if (currentUser != user)
        {
            currentUser = user;
        }
    }
}

internal sealed class UserCircuitHandler(
        AuthenticationStateProvider authenticationStateProvider,
        UserService userService) 
        : CircuitHandler, IDisposable
{
    public override Task OnCircuitOpenedAsync(Circuit circuit, 
        CancellationToken cancellationToken)
    {
        authenticationStateProvider.AuthenticationStateChanged += 
            AuthenticationChanged;

        return base.OnCircuitOpenedAsync(circuit, cancellationToken);
    }

    private void AuthenticationChanged(Task<AuthenticationState> task)
    {
        _ = UpdateAuthentication(task);

        async Task UpdateAuthentication(Task<AuthenticationState> task)
        {
            try
            {
                var state = await task;
                userService.SetUser(state.User);
            }
            catch
            {
            }
        }
    }

    public override async Task OnConnectionUpAsync(Circuit circuit, 
        CancellationToken cancellationToken)
    {
        var state = await authenticationStateProvider.GetAuthenticationStateAsync();
        userService.SetUser(state.User);
    }

    public void Dispose()
    {
        authenticationStateProvider.AuthenticationStateChanged -= 
            AuthenticationChanged;
    }
}
using System.Security.Claims;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Server.Circuits;

public class UserService
{
    private ClaimsPrincipal currentUser = new ClaimsPrincipal(new ClaimsIdentity());

    public ClaimsPrincipal GetUser()
    {
        return currentUser;
    }

    internal void SetUser(ClaimsPrincipal user)
    {
        if (currentUser != user)
        {
            currentUser = user;
        }
    }
}

internal sealed class UserCircuitHandler : CircuitHandler, IDisposable
{
    private readonly AuthenticationStateProvider authenticationStateProvider;
    private readonly UserService userService;

    public UserCircuitHandler(
        AuthenticationStateProvider authenticationStateProvider,
        UserService userService)
    {
        this.authenticationStateProvider = authenticationStateProvider;
        this.userService = userService;
    }

    public override Task OnCircuitOpenedAsync(Circuit circuit, 
        CancellationToken cancellationToken)
    {
        authenticationStateProvider.AuthenticationStateChanged += 
            AuthenticationChanged;

        return base.OnCircuitOpenedAsync(circuit, cancellationToken);
    }

    private void AuthenticationChanged(Task<AuthenticationState> task)
    {
        _ = UpdateAuthentication(task);

        async Task UpdateAuthentication(Task<AuthenticationState> task)
        {
            try
            {
                var state = await task;
                userService.SetUser(state.User);
            }
            catch
            {
            }
        }
    }

    public override async Task OnConnectionUpAsync(Circuit circuit, 
        CancellationToken cancellationToken)
    {
        var state = await authenticationStateProvider.GetAuthenticationStateAsync();
        userService.SetUser(state.User);
    }

    public void Dispose()
    {
        authenticationStateProvider.AuthenticationStateChanged -= 
            AuthenticationChanged;
    }
}

I filen Program:

using Microsoft.AspNetCore.Components.Server.Circuits;
using Microsoft.Extensions.DependencyInjection.Extensions;

...

builder.Services.AddScoped<UserService>();
builder.Services.TryAddEnumerable(
    ServiceDescriptor.Scoped<CircuitHandler, UserCircuitHandler>());

I Startup.ConfigureServices av Startup.cs:

using Microsoft.AspNetCore.Components.Server.Circuits;
using Microsoft.Extensions.DependencyInjection.Extensions;

...

services.AddScoped<UserService>();
services.TryAddEnumerable(
    ServiceDescriptor.Scoped<CircuitHandler, UserCircuitHandler>());

Använd tjänsten i en komponent för att hämta användaren:

@inject UserService UserService

<h1>Hello, @(UserService.GetUser().Identity?.Name ?? "world")!</h1>

För att ange användaren i mellanlagring för MVC, "Razor Pages", och i andra ASP.NET Core-scenarier, anropa SetUserUserService i en anpassad mellanlagring efter att autentiseringsmellanlagringen har körts, eller ange användaren med en IClaimsTransformation implementation. I följande exempel används middlewaremetoden.

UserServiceMiddleware.cs:

public class UserServiceMiddleware
{
    private readonly RequestDelegate next;

    public UserServiceMiddleware(RequestDelegate next)
    {
        this.next = next ?? throw new ArgumentNullException(nameof(next));
    }

    public async Task InvokeAsync(HttpContext context, UserService service)
    {
        service.SetUser(context.User);
        await next(context);
    }
}

Anropa mellanprogrammet direkt innan anropet till app.MapRazorComponents<App>() i filen Program:

Anropa mellanprogrammet direkt innan anropet till app.MapBlazorHub() i filen Program:

Anropa mellanprogrammet omedelbart före anropet till app.MapBlazorHub() i Startup.Configure av Startup.cs:

app.UseMiddleware<UserServiceMiddleware>();

Åtkomst AuthenticationStateProvider i middleware för utgående förfrågningar

AuthenticationStateProvider från en DelegatingHandler för HttpClient som skapats med IHttpClientFactory kan nås i mellanprogram för utgående begäran med hjälp av en kretsaktivitetshanterare.

Notera

Allmän vägledning om hur du definierar delegering av hanterare för HTTP-begäranden av HttpClient instanser som skapats med IHttpClientFactory i ASP.NET Core-appar finns i följande avsnitt i Gör HTTP-begäranden med IHttpClientFactory i ASP.NET Core:

I följande exempel används AuthenticationStateProvider för att koppla ett anpassat användarnamnhuvud för autentiserade användare till utgående begäranden.

Implementera klassen CircuitServicesAccessor först i följande avsnitt i artikeln Blazor beroendeinjektion (DI):

Åtkomst till Blazor tjänster på serversidan från ett annat DI-omfång

Använd CircuitServicesAccessor för att komma åt AuthenticationStateProvider i implementeringen av DelegatingHandler.

AuthenticationStateHandler.cs:

using Microsoft.AspNetCore.Components.Authorization;

public class AuthenticationStateHandler(
    CircuitServicesAccessor circuitServicesAccessor) 
    : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var authStateProvider = circuitServicesAccessor.Services?
            .GetRequiredService<AuthenticationStateProvider>();

        if (authStateProvider is null)
        {
            throw new Exception("AuthenticationStateProvider not available");
        }

        var authState = await authStateProvider.GetAuthenticationStateAsync();

        var user = authState?.User;

        if (user?.Identity is not null && user.Identity.IsAuthenticated)
        {
            request.Headers.Add("X-USER-IDENTITY-NAME", user.Identity.Name);
        }

        return await base.SendAsync(request, cancellationToken);
    }
}

I filen Program registrerar du AuthenticationStateHandler och lägger till hanteraren i IHttpClientFactory som skapar HttpClient instanser:

builder.Services.AddTransient<AuthenticationStateHandler>();

builder.Services.AddHttpClient("HttpMessageHandler")
    .AddHttpMessageHandler<AuthenticationStateHandler>();