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. För den aktuella utgåvan, se .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.
För den aktuella utgåvan, se .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.
Not
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å ASP.NET Core 5.0 eller tidigare tar du bort null-typbeteckningen (?
) från string?
, TodoItem[]?
, WeatherForecast[]?
och IEnumerable<GitHubBranch>?
typerna i artikelns exempel.
Skicka tokens till en Blazor-app på server-sidan
För Blazor Web Appska vägledning om hur du skickar token till Razor komponenter hanteras i dokumentationen (dotnet/AspNetCore.Docs
#31691) 2025 för en förhandsversion av .NET 10.
För mer information, se följande ämnen:
-
Access
AuthenticationStateProvider
i mellanprogram för utgående begäran (dotnet/aspnetcore
#52379): Det här är det aktuella problemet med att åtgärda överföring av tokens i Blazor Web Apps, för närvarande schemalagd för .NET 10 (sent 2025). -
Problem med att tillhandahålla åtkomsttoken till HttpClient i interaktivt serverläge (
dotnet/aspnetcore
#52390): Det här problemet stängdes som en dubblett av föregående problem, men det innehåller användbara diskussionsstrategier och potentiella lösningar.
För Blazor Serverkan du visa 7.0-versionen av den här artikeln.
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.
Observera
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.cs
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.
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
, skapa en instans av InitialApplicationState
och skicka den som en parameter till appen.
I filen Pages/_Host.cshtml
, skapa en instans av InitialApplicationState
och skicka den 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();
}
}
Not
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.
Not
Mer information om hur du lägger till paket i .NET-appar finns i artiklarna under Installera och hantera paket på Arbetsflö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;
var request = new HttpRequestMessage(HttpMethod.Get,
"https://localhost:5003/WeatherForecast");
request.Headers.Add("Authorization", $"Bearer {token}");
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 5.0 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 5.0 konfigurerar du alternativet OpenIdConnectOptions.Authority 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
AuthenticationChanged
för att uppdatera användaren. - Blocket
catch
för aktivitetenUpdateAuthentication
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 SetUser
på UserService
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.
Not
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
:
public class AuthenticationStateHandler(
CircuitServicesAccessor circuitServicesAccessor)
: DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
var authStateProvider = circuitServicesAccessor.Services
.GetRequiredService<AuthenticationStateProvider>();
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>();
ASP.NET Core