Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Remarque
Ceci n’est pas la dernière version de cet article. Pour la version actuelle, consultez la version .NET 10 de cet article.
Avertissement
Cette version d’ASP.NET Core n’est plus prise en charge. Pour plus d’informations, consultez la stratégie de support .NET et .NET Core. Pour la version actuelle, consultez la version .NET 10 de cet article.
Cet article explique comment configurer Blazor côté serveur pour d’autres scénarios de sécurité, notamment comment passer des jetons à une application Blazor.
Remarque
Les exemples de code de cet article utilisent les types de référence null (NRT, nullable reference types) et l'analyse statique de l'état null du compilateur .NET, qui sont pris en charge dans ASP.NET Core 6 et ses versions ultérieures. Lorsque vous ciblez .NET 5 ou une version antérieure, supprimez la désignation de type null des types ?, string?, TodoItem[]? et WeatherForecast[]? dans les exemples de l'article.
Passer des jetons à une application Blazor côté serveur
Cette section s’applique à Blazor Web Apps. Pour Blazor Server, consultez la version .NET 7 de cet article.
Si vous souhaitez simplement utiliser des jetons d’accès pour effectuer des appels d’API web à partir d’un Blazor Web Appclient HTTP nommé, consultez la section Utiliser un gestionnaire de jetons pour les appels d’API web , qui explique comment utiliser une DelegatingHandler implémentation pour attacher le jeton d’accès d’un utilisateur aux requêtes sortantes. Les instructions suivantes de cette section concernent les développeurs qui ont besoin de jetons d’accès, de jetons d’actualisation et d’autres propriétés d’authentification côté serveur à d’autres fins.
Pour enregistrer des jetons et d’autres propriétés d’authentification pour une utilisation côté serveur dans Blazor Web Apps, nous vous recommandons d’utiliser IHttpContextAccessor/HttpContext (IHttpContextAccessor, HttpContext). Lecture des jetons à partir de HttpContext, y compris en tant que paramètre en cascade, à l'aide de IHttpContextAccessor est prise en charge pour obtenir des jetons à utiliser pendant le rendu interactif du serveur, si les jetons sont obtenus lors du rendu côté serveur statique (SSR statique) ou du prérendu. Toutefois, les jetons ne sont pas mis à jour si l'utilisateur s'authentifie après que le circuit a été établi, car HttpContext est capturé au début de la connexion SignalR. En outre, l’utilisation de AsyncLocal<T> par IHttpContextAccessor signifie que vous devez faire attention à ne pas perdre le contexte d’exécution avant de lire le HttpContext. Pour plus d'informations, consultez IHttpContextAccessor/HttpContext dans les applications ASP.NET Core Blazor.
Dans une classe de service, obtenez l’accès aux membres de l’espace de noms Microsoft.AspNetCore.Authentication pour exposer la GetTokenAsync méthode sur HttpContext. Une autre approche, qui est commentée dans l’exemple suivant, consiste à appeler AuthenticateAsync sur HttpContext. Pour la restitution de AuthenticateResult.Properties, appelez 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;
}
}
Le service est inscrit dans le fichier du Program projet serveur :
builder.Services.AddScoped<AuthenticationProcessor>();
AuthenticationProcessor peut être injecté dans des services côté serveur, par exemple dans un DelegatingHandler pour un HttpClient préconfiguré. L’exemple suivant est uniquement à des fins de démonstration ou, si vous devez effectuer un traitement spécial dans le AuthenticationProcessor service, car vous pouvez simplement injecter IHttpContextAccessor et obtenir le jeton directement pour appeler des API web externes (pour plus d’informations sur l’utilisation IHttpContextAccessor directe pour appeler des API web, consultez la section Utiliser un gestionnaire de jetons pour les appels d’API web ).
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);
}
}
Le gestionnaire de jetons est inscrit et agit comme gestionnaire de délégation pour un client HTTP nommé dans le Program fichier :
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>();
Avertissement
Assurez-vous que les jetons ne sont jamais transmis et gérés par le client (le .Client projet), par exemple, dans un composant qui adopte le rendu automatique interactif et est rendu sur le client ou par un service côté client. Demandez toujours au client d’appeler le serveur (projet) pour traiter les demandes avec des jetons.
Les jetons et autres données d’authentification ne doivent jamais quitter le serveur.
Pour les composants interactifs automatiques, consultez l’authentification et l’autorisation ASP.NET CoreBlazor, qui montre comment laisser des jetons d’accès et d’autres propriétés d’authentification sur le serveur. Envisagez également d’adopter le modèle Backend-for-Frontend (BFF), qui adopte une structure d’appels similaire et est décrit dans Secure an ASP.NET Core Blazor Web App avec OpenID Connect (OIDC) pour les fournisseurs OIDC et Secure an ASP.NET Core Blazor Web App avec Microsoft Entra ID pour MicrosoftIdentity Web avec Entra.
Utiliser un gestionnaire de jetons pour les appels d’API web
L’approche suivante vise à attacher le jeton d’accès d’un utilisateur aux requêtes sortantes, en particulier pour effectuer des appels d’API web à des applications API web externes. L’approche est illustrée pour un Blazor Web App qui adopte le rendu global de l'Interactive Server, mais la même approche générale s’applique aux Blazor Web Apps qui adoptent le mode global de rendu de l'Interactive Auto. Le concept important à garder à l'esprit est que l'accès à HttpContext en utilisant IHttpContextAccessor n'est effectué que sur le serveur.
Pour obtenir une démonstration des instructions de cette section, consultez les BlazorWebAppOidc exemples d’applications BlazorWebAppOidcServer (.NET 8 ou version ultérieure) dans le Blazor référentiel GitHub d’exemples. Les exemples adoptent un mode de rendu interactif global et une authentification OIDC avec Microsoft Entra sans utiliser de packages spécifiques à Entra. Les exemples montrent comment passer un jeton d’accès JWT pour appeler une API web sécurisée.
La plateforme d’identités Microsoft avec des paquets Web Microsoft Identity pour Microsoft Entra ID fournit une API permettant d’appeler des API web à partir de Blazor Web Apps avec la gestion et le renouvellement automatiques des jetons. Pour plus d’informations, consultez Sécurisez un ASP.NET Core Blazor Web App avec l’ID Microsoft Entra et les applications d'exemple BlazorWebAppEntraBlazorWebAppEntraBff (.NET 9 ou version ultérieure) dans le Blazor dépôt GitHub.
Sous-classe DelegatingHandler pour attacher le jeton d’accès d’un utilisateur aux requêtes sortantes. Le gestionnaire de jetons s’exécute uniquement sur le serveur. L’utilisation HttpContext est donc sécurisée.
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);
}
}
Remarque
Pour obtenir des conseils sur comment accéder à un AuthenticationStateProvider depuis un DelegatingHandler, consultez la section Accès AuthenticationStateProvider dans le middleware de requête sortante .
Dans le fichier du Program projet, le gestionnaire de jetons (TokenHandler) est inscrit en tant que service limité et spécifié en tant que gestionnaire de messages du client HTTP nommé avec AddHttpMessageHandler.
Dans l’exemple suivant, l’espace réservé {HTTP CLIENT NAME} est le nom du HttpClient, et l’URI {BASE ADDRESS} est l'adresse de base de l'API Web. Pour plus d’informations sur AddHttpContextAccessor, consultez IHttpContextAccessor/HttpContext dans les applications ASP.NET CoreBlazor.
Dans Program.cs :
builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped<TokenHandler>();
builder.Services.AddHttpClient("{HTTP CLIENT NAME}",
client => client.BaseAddress = new Uri("{BASE ADDRESS}"))
.AddHttpMessageHandler<TokenHandler>();
Exemple:
builder.Services.AddScoped<TokenHandler>();
builder.Services.AddHttpClient("ExternalApi",
client => client.BaseAddress = new Uri("https://localhost:7277"))
.AddHttpMessageHandler<TokenHandler>();
Vous pouvez fournir l’adresse de base du client HTTP à partir de la configuration avec builder.Configuration["{CONFIGURATION KEY}"], où le {CONFIGURATION KEY} sert de clé de configuration :
new Uri(builder.Configuration["ExternalApiUri"] ?? throw new IOException("No URI!"))
Dans appsettings.json, spécifiez le ExternalApiUri. L’exemple suivant définit la valeur sur l’adresse localhost de l’API https://localhost:7277web externe sur :
"ExternalApiUri": "https://localhost:7277"
À ce stade, un HttpClient composant créé peut effectuer des requêtes d’API web sécurisées. Dans l'exemple suivant, le {REQUEST URI} est l'URI de requête relative, et le {HTTP CLIENT NAME} est le nom du HttpClient.
using var request = new HttpRequestMessage(HttpMethod.Get, "{REQUEST URI}");
var client = ClientFactory.CreateClient("{HTTP CLIENT NAME}");
using var response = await client.SendAsync(request);
Exemple:
using var request = new HttpRequestMessage(HttpMethod.Get, "/weather-forecast");
var client = ClientFactory.CreateClient("ExternalApi");
using var response = await client.SendAsync(request);
Des fonctionnalités supplémentaires sont planifiées pour Blazor, qui sont suivies par Access AuthenticationStateProvider dans le middleware de requête sortante (dotnet/aspnetcore #52379).
Le problème de fourniture d’un jeton d’accès à HttpClient en mode serveur interactif (dotnet/aspnetcore #52390) est un problème fermé qui contient des discussions utiles et des stratégies de contournement potentielles pour les cas d’usage avancés.
Les jetons disponibles en dehors des composants Razor d’une application Blazor côté serveur peuvent être passés aux composants selon l’approche décrite dans cette section. L’exemple de cette section se concentre sur le passage d’accès, d’actualisation et de jeton de falsification de requête (XSRF) jetons à l’application Blazor, mais l’approche est valide pour d’autres états de contexte HTTP.
Remarque
Le passage du jeton XSRF à Razor composants est utile dans les scénarios où les composants POST vers Identity ou d’autres points de terminaison nécessitant une validation. Si votre application nécessite uniquement des jetons d’accès et d’actualisation, vous pouvez supprimer le code de jeton XSRF de l’exemple suivant.
Authentifiez l’application comme vous le feriez avec une application Pages ou MVC Razor standard. Provisionnez et enregistrez les jetons dans l’authentification cookie.
Dans le fichier 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);
});
Dans 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);
});
Dans 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);
});
En option, des étendues supplémentaires sont ajoutées avec options.Scope.Add("{SCOPE}");, où l’espace réservé {SCOPE} est l’étendue supplémentaire à ajouter.
Définissez un service de fournisseur de jetons délimités qui peut être utilisé dans l’application Blazor pour convertir les jetons à partir de l’injection de dépendances.
TokenProvider.cs :
public class TokenProvider
{
public string? AccessToken { get; set; }
public string? RefreshToken { get; set; }
public string? XsrfToken { get; set; }
}
Dans le fichier Program, ajoutez des services à :
-
IHttpClientFactory : utilisé dans une classe
WeatherForecastServicequi obtient des données météorologiques à partir d’une API de serveur avec un jeton d’accès. -
TokenProvider: contient les jetons d’accès et d’actualisation.
builder.Services.AddHttpClient();
builder.Services.AddScoped<TokenProvider>();
Dans Startup.ConfigureServices de Startup.cs, ajoutez des services pour :
-
IHttpClientFactory : utilisé dans une classe
WeatherForecastServicequi obtient des données météorologiques à partir d’une API de serveur avec un jeton d’accès. -
TokenProvider: contient les jetons d’accès et d’actualisation.
services.AddHttpClient();
services.AddScoped<TokenProvider>();
Définissez une classe à passer dans l’état initial de l’application avec les jetons d’accès et d’actualisation.
InitialApplicationState.cs :
public class InitialApplicationState
{
public string? AccessToken { get; set; }
public string? RefreshToken { get; set; }
public string? XsrfToken { get; set; }
}
Dans le fichier Pages/_Host.cshtml, créez une instance de InitialApplicationState et passez-la en tant que paramètre à l’application :
Dans le fichier Pages/_Layout.cshtml, créez une instance de InitialApplicationState et passez-la en tant que paramètre à l’application :
Dans le fichier Pages/_Host.cshtml, créez une instance de InitialApplicationState et passez-la en tant que paramètre à l’application :
@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" ... />
Dans le composant App (App.razor), convertissez le service et initialisez-le avec les données du paramètre :
@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();
}
}
Remarque
Une alternative à l’attribution de l’état initial à TokenProvider dans l’exemple précédent consiste à copier les données dans un service délimité dans OnInitializedAsync pour une utilisation dans l’application.
Ajoutez une référence de package à l’application pour le package NuGet Microsoft.AspNet.WebApi.Client.
Remarque
Pour obtenir des conseils sur l’ajout de packages à des applications .NET, consultez les articles figurant sous Installer et gérer des packages dans Flux de travail de la consommation des packages (documentation NuGet). Vérifiez les versions du package sur NuGet.org.
Dans le service qui effectue une requête d’API sécurisée, injectez le fournisseur de jetons et récupérez le jeton pour la requête d’API :
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>();
}
}
Pour un jeton XSRF passé à un composant, injectez le TokenProvider et ajoutez le jeton XSRF à la requête POST. L’exemple suivant ajoute le jeton à un point de terminaison de déconnexion POST. Le scénario de l’exemple suivant est que le point de terminaison de déconnexion (Areas/Identity/Pages/Account/Logout.cshtml, généré automatiquement dans l’application) ne spécifie pas de IgnoreAntiforgeryTokenAttribute (@attribute [IgnoreAntiforgeryToken]), car il effectue une action en plus d’une opération de déconnexion normale qui doit être protégée. Le point de terminaison nécessite un jeton XSRF valide pour traiter correctement la demande.
Dans un composant qui présente un bouton Déconnexion aux utilisateurs autorisés :
@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>
Définir le schéma d’authentification
Pour une application qui utilise plusieurs intergiciels d’authentification et qui a donc plusieurs schémas d’authentification, le schéma utilisé par Blazor peut être explicitement défini dans la configuration du point de terminaison du fichier Program. L’exemple suivant définit le schéma OpenID Connect (OIDC) :
Pour une application qui utilise plusieurs intergiciels d’authentification et qui a donc plusieurs schémas d’authentification, le schéma utilisé par Blazor peut être explicitement défini dans la configuration du point de terminaison de Startup.cs. L’exemple suivant définit le schéma OpenID Connect (OIDC) :
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
});
Pour une application qui utilise plusieurs intergiciels d’authentification et qui a donc plusieurs schémas d’authentification, le schéma utilisé par Blazor peut être explicitement défini dans la configuration du point de terminaison de Startup.Configure. L’exemple suivant définit le schéma Microsoft Entra ID :
endpoints.MapBlazorHub().RequireAuthorization(
new AuthorizeAttribute
{
AuthenticationSchemes = AzureADDefaults.AuthenticationScheme
});
Utiliser des points de terminaison OpenID Connect (OIDC) v2.0
Dans les versions de ASP.NET Core antérieures à .NET 5, la bibliothèque d’authentification et Blazor les modèles utilisent des points de terminaison OpenID Connect (OIDC) v1.0. Pour utiliser un point de terminaison v2.0 avec des versions de ASP.NET Core antérieures à .NET 5, configurez l’option OpenIdConnectOptions.Authority dans :OpenIdConnectOptions
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme,
options =>
{
options.Authority += "/v2.0";
}
Vous pouvez également définir le paramètre dans le fichier de paramètres de l’application (appsettings.json) :
{
"AzureAd": {
"Authority": "https://login.microsoftonline.com/common/oauth2/v2.0",
...
}
}
Si l’ajout d’un segment à l’autorité n’est pas approprié pour le fournisseur OIDC de l’application, par exemple avec des fournisseurs non-ME-ID, définissez la propriété Authority directement. Définissez la propriété dans OpenIdConnectOptions ou dans le fichier de paramètres de l’application avec la clé Authority.
Modifications du code
La liste des revendications dans le jeton d’ID change pour les points de terminaison v2.0. La documentation Microsoft sur les modifications a été mise hors service, mais de l’aide sur les revendications dans un jeton d’ID est disponible dans la référence des revendications de jeton d’ID.
Étant donné que les ressources sont spécifiées dans les URI d’étendue pour les points de terminaison v2.0, supprimez le paramètre de la propriété OpenIdConnectOptions.Resource dans OpenIdConnectOptions :
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options => { ... options.Resource = "..."; // REMOVE THIS LINE ... }
URI ID d’application
- Lorsque vous utilisez des points de terminaison v2.0, les API définissent un
App ID URIdestiné à représenter un identificateur unique de l’API. - Toutes les étendues incluent l’URI d’ID d’application comme préfixe, et les points de terminaison v2.0 émettent des jetons d’accès avec l’URI d’ID d’application comme public.
- Lorsque vous utilisez des points de terminaison V2.0, l’ID client configuré dans l’API serveur passe de l’ID d’application d’API (ID client) à l’URI d’ID d’application.
appsettings.json :
{
"AzureAd": {
...
"ClientId": "https://{TENANT}.onmicrosoft.com/{PROJECT NAME}"
...
}
}
Vous trouverez l’URI d’ID d’application à utiliser dans la description d’inscription à l’application du fournisseur OIDC.
Gestionnaire de circuit pour capturer des utilisateurs des services personnalisés
Utilisez un CircuitHandler pour capturer un utilisateur à partir du AuthenticationStateProvider et définissez l’utilisateur dans un service. Si vous souhaitez mettre à jour l’utilisateur, inscrivez un rappel vers AuthenticationStateChanged et empilez une Task pour obtenir le nouvel utilisateur et mettre à jour le service. L'exemple suivant illustre l’approche.
Dans l’exemple suivant :
-
OnConnectionUpAsync est appelé chaque fois que le circuit se reconnecte, définissant l’utilisateur pour la durée de vie de la connexion. Seule la méthode OnConnectionUpAsync est obligatoire, sauf si vous implémentez des mises à jour via un gestionnaire des modifications de l’authentification (
AuthenticationChangeddans l’exemple suivant). -
OnCircuitOpenedAsync est appelé pour joindre le gestionnaire de l’authentification modifiée,
AuthenticationChanged, pour mettre à jour l’utilisateur. - Le bloc
catchde la tâcheUpdateAuthenticationn’effectue aucune action sur les exceptions, car il n’existe aucun moyen de les signaler à ce stade de l’exécution du code. Si une exception est levée à partir de la tâche, l’exception est signalée ailleurs dans l’application.
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;
}
}
Dans le fichier Program :
using Microsoft.AspNetCore.Components.Server.Circuits;
using Microsoft.Extensions.DependencyInjection.Extensions;
...
builder.Services.AddScoped<UserService>();
builder.Services.TryAddEnumerable(
ServiceDescriptor.Scoped<CircuitHandler, UserCircuitHandler>());
Dans Startup.ConfigureServices de Startup.cs :
using Microsoft.AspNetCore.Components.Server.Circuits;
using Microsoft.Extensions.DependencyInjection.Extensions;
...
services.AddScoped<UserService>();
services.TryAddEnumerable(
ServiceDescriptor.Scoped<CircuitHandler, UserCircuitHandler>());
Utilisez le service dans un composant pour obtenir l’utilisateur :
@inject UserService UserService
<h1>Hello, @(UserService.GetUser().Identity?.Name ?? "world")!</h1>
Pour définir l’utilisateur dans l’intergiciel pour MVC, Pages Razor et dans d’autres scénarios d’ASP.NET Core, appelez SetUser sur le UserService dans l’intergiciel personnalisé une fois l’intergiciel d’authentification exécuté ou définissez l’utilisateur avec une implémentation IClaimsTransformation. L’exemple suivant adopte l’approche d’intergiciel.
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);
}
}
Juste avant l’appel à app.MapRazorComponents<App>() dans le fichier Program, appelez l’intergiciel :
Juste avant l’appel à app.MapBlazorHub() dans le fichier Program, appelez l’intergiciel :
Appelez l’intergiciel juste avant l’appel à app.MapBlazorHub() dans Startup.Configure ou Startup.cs :
app.UseMiddleware<UserServiceMiddleware>();
Accès AuthenticationStateProvider dans l’intergiciel de requête sortante
Le AuthenticationStateProvider d’un DelegatingHandler pour HttpClient créé avec IHttpClientFactory est accessible dans un intergiciel de requête sortant à l’aide d’un gestionnaire d’activités de circuit.
Remarque
Pour obtenir des conseils généraux sur la définition de gestionnaires pour les requêtes HTTP par HttpClient instances créées à l’aide de IHttpClientFactory dans les applications ASP.NET Core, consultez les sections suivantes de Effectuer des requêtes HTTP à l’aide d’IHttpClientFactory dans ASP.NET Core :
L’exemple suivant utilise AuthenticationStateProvider pour attacher un en-tête de nom d’utilisateur personnalisé pour les utilisateurs authentifiés aux requêtes sortantes.
Tout d’abord, implémentez la classe CircuitServicesAccessor dans la section suivante de l’article d’injection de dépendances (DI) Blazor :
Accéder aux services côté serveurBlazor à partir d’une autre étendue de DI
Utilisez le CircuitServicesAccessor pour accéder au AuthenticationStateProvider dans l’implémentation 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);
}
}
Dans le fichier Program, inscrivez AuthenticationStateHandler et ajoutez le gestionnaire à IHttpClientFactory qui crée des instances HttpClient :
builder.Services.AddTransient<AuthenticationStateHandler>();
builder.Services.AddHttpClient("HttpMessageHandler")
.AddHttpMessageHandler<AuthenticationStateHandler>();