注意
這不是這篇文章的最新版本。 如需目前的版本,請參閱 本文的 .NET 9 版本。
警告
不再支援此版本的 ASP.NET Core。 如需詳細資訊,請參閱 .NET 和 .NET Core 支持原則。 如需目前的版本,請參閱 本文的 .NET 9 版本。
此文章說明如何針對額外的安全性情境設定伺服器端 Blazor,包括如何將憑證傳遞至 Blazor 應用程式。
注意
本文中的程式碼範例採用 可空值參考型別 (NRT) 與 .NET 編譯器的空值狀態靜態分析,這在 .NET 6 或更新版本的 ASP.NET Core 中受到支援。 以 .NET 5 或更早版本為目標時,請從文章範例中的?
、string?
、TodoItem[]?
和WeatherForecast[]?
類型中移除空類型指定(IEnumerable<GitHubBranch>?
)。
將令牌傳遞至伺服器端 Blazor 應用程式
本節適用於 Blazor Web Apps。 針對 Blazor Server,檢視 本文的 .NET 7 版本一節。
如果您只想使用存取令牌透過Blazor Web App進行 Web API 呼叫,請參閱使用DelegatingHandler Web API 呼叫的令牌處理程式一節,其中說明如何使用 實作將使用者的存取令牌附加至傳出要求。 本節中的下列指引適用於需要存取令牌、重新整理令牌和其他驗證屬性伺服器端以用於其他用途的開發人員。
若要將令牌和其他身份驗證屬性儲存於 Blazor Web App 中以便於伺服器端使用,建議使用 IHttpContextAccessor
/HttpContext
(IHttpContextAccessor、HttpContext)。 從 HttpContext讀取令牌,包括作為 串聯參數,使用 IHttpContextAccessor 支援在獲取令牌的條件下,於靜態伺服器端渲染(靜態 SSR)或預渲染期間,於互動式伺服器渲染時使用這些令牌。 不過,如果使用者在電路建立後進行驗證,令牌將不會更新,因為 HttpContext 是在連線開始時 SignalR 捕獲的。 此外,使用 AsyncLocal<T>IHttpContextAccessor 意味著在讀取 HttpContext之前,您必須小心不要丟失執行上下文。 如需詳細資訊,請參閱 ASP.NET Core Blazor 應用程式中 IHttpContextAccessor/HttpContext。
在服務類別中,取得命名空間Microsoft.AspNetCore.Authentication成員的存取權,以便在GetTokenAsync上呈現HttpContext方法。 在下列範例中,已被註解掉的替代方法是呼叫 AuthenticateAsync 在 HttpContext 上。 針對傳回的 AuthenticateResult.Properties,呼叫 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;
}
}
服務會在伺服器項目的 Program
檔案中註冊:
builder.Services.AddScoped<AuthenticationProcessor>();
AuthenticationProcessor
可以插入伺服器端服務中,例如,針對預先設定的 DelegatingHandler 中的 HttpClient。 下列範例僅供示範之用,或如果您需要在服務中AuthenticationProcessor
執行特殊處理,因為您可以直接插入IHttpContextAccessor並取得令牌以呼叫外部 Web API(如需直接呼叫 Web API 的詳細資訊,請參閱IHttpContextAccessor Web API 呼叫的令牌處理程式一節)。
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);
}
}
令牌處理程式已註冊,並在Program
檔案中作為具名 HTTP 用戶端的委派處理程式:
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>();
謹慎
確保令牌永遠不會在用戶端(例如 .Client
項目中)傳輸或處理,例如在採用互動式自動渲染的元件中,以及由用戶端或用戶端服務渲染的情況下。 務必讓用戶端呼叫伺服器(專案)來使用令牌處理要求。
令牌和其他驗證數據不應該離開伺服器。
如需互動式自動元件,請參閱 ASP.NET 核心 Blazor 驗證和授權,示範如何在伺服器上保留存取令牌和其他驗證屬性。 此外,請考慮採用 Backend-for-Frontend (BFF) 模式,其採用類似的呼叫結構,如使用 OIDC 提供者的 OpenID Connect 保護 ASP.NET CoreBlazor Web App(OIDC),以及使用 Entra 保護具有 Microsoft Entra ID Microsoft Web 的 ASP.NET Core Blazor Web AppIdentity。
使用令牌處理程序進行 Web API 呼叫
下列方法旨在將使用者的存取令牌附加至外部請求,特別是進行 Web API 呼叫,以與外部 Web API 應用程式溝通。 採用全域互動式伺服器轉譯的Blazor Web App會示範該方法,但相同的一般方法也適用於採用全域互動式自動轉譯模式的Blazor Web App。 請記住的重要概念是,使用HttpContext來存取IHttpContextAccessor只會在伺服器上執行。
如需本節中指引的示範,請參閱 BlazorWebAppOidc
BlazorWebAppOidcServer
和 Blazor 範例應用程式 (.NET 8 或更新版本)。 這些範例會採用全域互動式轉譯模式,並使用 Microsoft Entra 進行 OIDC 驗證,而不需使用 Entra 特定的套件。 這些範例示範如何傳遞 JWT 存取令牌來呼叫安全的 Web API。
Microsoft 身分識別平臺 配合 Microsoft Identity Web 套件 針對 Microsoft Entra ID 提供 API,從 Blazor Web App 提供自動令牌管理和更新的 Web API 呼叫功能。 如需詳細資訊,請參閱 GitHub 範例存放庫中的 使用 Microsoft Entra ID 保護 ASP.NET Core Blazor Web App 以及 BlazorWebAppEntra
BlazorWebAppEntraBff
範例應用程式(.NET 9 或更新版本)。
子類別 DelegatingHandler ,將使用者的存取令牌附加至傳出要求。 令牌處理程式只會在伺服器上執行,因此使用 HttpContext 是安全的。
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);
}
}
注意
如需如何從 AuthenticationStateProvider
存取 DelegatingHandler
的指引,請參閱傳出要求中間件中的存取AuthenticationStateProvider
一節。
在專案的 Program
檔案中,令牌處理程式(TokenHandler
)會被註冊為一個範圍服務,並且作為 具名 HTTP 客戶端 的訊息處理程式 AddHttpMessageHandler 進行指定。
在以下範例中,{HTTP CLIENT NAME}
佔位元是 HttpClient 的名稱,而 {BASE ADDRESS}
佔位元是 Web API 的基本位址 URI。 如需有關AddHttpContextAccessor的詳細資訊,請參閱 ASP.NET Core Blazor 應用程式中的 IHttpContextAccessor/HttpContext。
在 Program.cs
中:
builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped<TokenHandler>();
builder.Services.AddHttpClient("{HTTP CLIENT NAME}",
client => client.BaseAddress = new Uri("{BASE ADDRESS}"))
.AddHttpMessageHandler<TokenHandler>();
範例:
builder.Services.AddScoped<TokenHandler>();
builder.Services.AddHttpClient("ExternalApi",
client => client.BaseAddress = new Uri("https://localhost:7277"))
.AddHttpMessageHandler<TokenHandler>();
您可以從配置提供的 HTTP 用戶端基址中取得,其中builder.Configuration["{CONFIGURATION KEY}"]
佔位符是配置鍵:
new Uri(builder.Configuration["ExternalApiUri"] ?? throw new IOException("No URI!"))
在 appsettings.json
中指定 ExternalApiUri
。 下列範例會將值設定為外部 Web API 的 localhost 位址:https://localhost:7277
。
"ExternalApiUri": "https://localhost:7277"
此時,元件所建立的 HttpClient 可以提出安全的 Web API 要求。 在下列範例中,{REQUEST URI}
是相對要求的 URI,而 {HTTP CLIENT NAME}
佔位元符是 HttpClient 的名稱。
using var request = new HttpRequestMessage(HttpMethod.Get, "{REQUEST URI}");
var client = ClientFactory.CreateClient("{HTTP CLIENT NAME}");
using var response = await client.SendAsync(request);
範例:
using var request = new HttpRequestMessage(HttpMethod.Get, "/weather-forecast");
var client = ClientFactory.CreateClient("ExternalApi");
using var response = await client.SendAsync(request);
已針對 規劃 Blazor其他功能,這些功能由 Access AuthenticationStateProvider
在傳出要求中間件中追蹤 (dotnet/aspnetcore
#52379) 。
在互動式伺服器模式中提供存取令牌給 HttpClient 的問題,dotnet/aspnetcore
是一個已關閉的議題,其中包含有益的討論和針對進階使用案例的潛在解決策略。
在伺服器端應用程式的 Razor 元件外部可用的 Token,可以按照本節所述的方法傳遞到元件。 此節中的範例主要介紹如何將存取權杖、重新整理權杖和防止偽造要求 (XSRF) 權杖傳遞至 Blazor 應用程式,但此方法適用於其他 HTTP 內容狀態。
注意
在元件 POST 至 Razor 或其他需要驗證之端點的情節下,將 XSRF 權杖傳遞至 Identity 元件會很有用。 如果您的應用程式只需要存取權杖和重新整理權杖,您可以從下列範例中移除 XSRF 權杖程式碼。
正如您使用一般的Razor Pages 或 MVC 應用程式所做的那樣對應用程式進行驗證。 提供並儲存憑證至驗證 cookie。
在 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);
});
在 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);
});
在 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);
});
您可以選擇性地使用 options.Scope.Add("{SCOPE}");
來新增其他範圍,其中 {SCOPE}
預留位置表示要新增的範圍。
在應用程式中定義一個限定範圍的權杖提供者服務,它可以用來透過Blazor解析權杖。
TokenProvider.cs
:
public class TokenProvider
{
public string? AccessToken { get; set; }
public string? RefreshToken { get; set; }
public string? XsrfToken { get; set; }
}
在 Program
檔案中,新增下列項目的服務:
-
IHttpClientFactory:用於使用存取權杖從伺服器 API 取得天氣資料的
WeatherForecastService
類別。 -
TokenProvider
:保留存取令牌和更新令牌。
builder.Services.AddHttpClient();
builder.Services.AddScoped<TokenProvider>();
在 Startup.ConfigureServices
的 Startup.cs
中,新增下列項目的服務:
-
IHttpClientFactory:用於使用存取權杖從伺服器 API 取得天氣資料的
WeatherForecastService
類別。 -
TokenProvider
:保留存取令牌和更新令牌。
services.AddHttpClient();
services.AddScoped<TokenProvider>();
定義類別,可以使用存取權杖和重新整理權杖來傳入初始應用程式狀態。
InitialApplicationState.cs
:
public class InitialApplicationState
{
public string? AccessToken { get; set; }
public string? RefreshToken { get; set; }
public string? XsrfToken { get; set; }
}
在 Pages/_Host.cshtml
檔案中,建立 InitialApplicationState
的執行個體,並將它當做參數傳遞給應用程式:
在 Pages/_Layout.cshtml
檔案中,建立 InitialApplicationState
的執行個體,並將它當做參數傳遞給應用程式:
在 Pages/_Host.cshtml
檔案中,建立 InitialApplicationState
的執行個體,並將它當做參數傳遞給應用程式:
@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" ... />
在 App
元件 (App.razor
) 中,解析服務並使用參數的資料將其初始化:
@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();
}
}
注意
將初始狀態指派給上述範例中之 TokenProvider
的替代方法是將資料複製到 OnInitializedAsync 內的限定範圍服務,以便在整個應用程式中使用。
為應用程式新增對 Microsoft.AspNet.WebApi.Client
NuGet 套件的參考。
在提出安全 API 要求的服務中,插入權杖提供者並擷取 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>();
}
}
針對傳遞至元件的 XSRF 權杖,請注入 TokenProvider
,並將該 XSRF 權杖新增至 POST 請求。 下列範例會將令牌新增至登出端點 POST。 下列範例的情境是登出端點 (Areas/Identity/Pages/Account/Logout.cshtml
,已在應用程式中產生架構) 不指定 IgnoreAntiforgeryTokenAttribute (@attribute [IgnoreAntiforgeryToken]
),因其除了要執行必須保護的正常登出作業之外,還額外執行了一些動作。 端點需要有效的 XSRF 權杖才能成功處理要求。
在向已授權的使用者顯示 [登出] 按鈕的元件中:
@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>
設定驗證配置
對於使用多個驗證中介軟體因而具有多個驗證配置的應用程式,可以在 Blazor 檔案的端點設定中明確設定 Program
所使用的配置。 下列範例會設定 OpenID Connect (OIDC) 配置:
對於使用多個驗證中介軟體因而具有多個驗證配置的應用程式,可以在 Blazor 的端點設定中明確設定 Startup.cs
所使用的配置。 下列範例會設定 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
});
對於使用多個驗證中介軟體因而具有多個驗證配置的應用程式,可以在 Blazor 的端點設定中明確設定 Startup.Configure
所使用的配置。 下列範例會設定 Microsoft Entra ID 方案:
endpoints.MapBlazorHub().RequireAuthorization(
new AuthorizeAttribute
{
AuthenticationSchemes = AzureADDefaults.AuthenticationScheme
});
使用 OpenID Connect (OIDC) v2.0 端點
在 .NET 5 之前的 ASP.NET Core 版本中,驗證連結庫和 Blazor 範本會使用 OpenID Connect (OIDC) v1.0 端點。 若要在 .NET 5 之前的 ASP.NET Core 版本中使用 v2.0 端點,請在OpenIdConnectOptions.Authority中設定OpenIdConnectOptions選項。
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme,
options =>
{
options.Authority += "/v2.0";
}
或者,您可以在應用程式設定 (appsettings.json
) 檔案中進行設定:
{
"AzureAd": {
"Authority": "https://login.microsoftonline.com/common/oauth2/v2.0/",
...
}
}
如果將區段附加到授權單位不適合應用程式的 OIDC 提供者 (例如使用非 ME 識別碼的提供者),請直接設定 Authority 屬性。 在 OpenIdConnectOptions 中設置屬性或在應用程式設定檔案中使用 Authority 鍵設置屬性。
程式碼變更
識別權杖中的宣告清單會針對版本 2.0 端點有所變更。 變更的 Microsoft 文件已淘汰,但識別碼權杖參考中提供了與識別碼權杖中的宣告有關的指引。
由於資源已在 v2.0 端點的範圍 URI 中指定,因此請移除 OpenIdConnectOptions.Resource 中的 OpenIdConnectOptions 屬性設定:
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options => { ... options.Resource = "..."; // REMOVE THIS LINE ... }
應用程式識別碼 URI
- 使用 v2.0 端點時,API 會定義
App ID URI
,用來代表 API 的唯一識別碼。 - 所有範圍都包含 App ID URI 作為前綴,而且 v2.0 端點會以 App ID URI 作為受眾發出存取權杖。
- 使用 V2.0 端點時,伺服器 API 中所設定的用戶端識別碼會從 API 應用程式識別碼 (用戶端識別碼) 變更為 App ID URI。
appsettings.json
:
{
"AzureAd": {
...
"ClientId": "https://{TENANT}.onmicrosoft.com/{PROJECT NAME}"
...
}
}
您可以在 OIDC 提供者應用程式註冊描述中,找到要使用的 App ID URI。
用來捕捉使用者以供自訂服務的電路處理程序
使用 CircuitHandler 從 AuthenticationStateProvider 擷取使用者,並在服務中設定使用者。 如果您想更新使用者,請註冊一個回撥函數到 AuthenticationStateChanged,並將 Task 加入佇列,以取得新的使用者並更新服務。 下列範例會示範此方法。
在以下範例中:
- 每次線路重新連線時都會呼叫 OnConnectionUpAsync,為連線的存留期設定使用者。 除非您透過處理常式來實作驗證變更的更新 (下列範例中的 OnConnectionUpAsync),否則只需要
AuthenticationChanged
方法。 - 呼叫 OnCircuitOpenedAsync 以附加驗證變更的處理常式
AuthenticationChanged
,以便更新使用者。 -
catch
工作的UpdateAuthentication
區塊不會對例外狀況採取任何動作,因為目前無法在執行程式碼時報告例外狀況。 如果從工作中擲回例外狀況,則會在應用程式的其他位置回報該例外狀況。
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;
}
}
在 Program
檔案中:
using Microsoft.AspNetCore.Components.Server.Circuits;
using Microsoft.Extensions.DependencyInjection.Extensions;
...
builder.Services.AddScoped<UserService>();
builder.Services.TryAddEnumerable(
ServiceDescriptor.Scoped<CircuitHandler, UserCircuitHandler>());
在 Startup.ConfigureServices
的 Startup.cs
中:
using Microsoft.AspNetCore.Components.Server.Circuits;
using Microsoft.Extensions.DependencyInjection.Extensions;
...
services.AddScoped<UserService>();
services.TryAddEnumerable(
ServiceDescriptor.Scoped<CircuitHandler, UserCircuitHandler>());
使用元件中的服務來取得使用者:
@inject UserService UserService
<h1>Hello, @(UserService.GetUser().Identity?.Name ?? "world")!</h1>
若要在 MVC、Razor Pages 和其他 ASP.NET Core 場景的中介軟體中設定使用者,請在驗證中介軟體執行後,於自訂中介軟體中對 SetUser
呼叫 UserService
,或使用 IClaimsTransformation 實作來設定使用者。 下列範例採用中介軟體方法。
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);
}
}
在即將呼叫 app.MapRazorComponents<App>()
檔案中的 Program
之前,呼叫中介軟體:
在即將呼叫 app.MapBlazorHub()
檔案中的 Program
之前,呼叫中介軟體:
在呼叫 app.MapBlazorHub()
中的 Startup.Configure
之前,預先呼叫中介軟體:
app.UseMiddleware<UserServiceMiddleware>();
在傳出要求的中介程式中存取 AuthenticationStateProvider
以 AuthenticationStateProvider 建立之 DelegatingHandlerHttpClient 中的 IHttpClientFactory,可以使用線路活動處理常式在傳出要求中介軟體中存取。
注意
如需取得一般指引,了解如何在 ASP.NET Core 應用程式中使用 HttpClient 建立的 IHttpClientFactory 執行個體來定義 HTTP 要求的委派處理常式,請參閱在 ASP.NET Core 中使用 IHttpClientFactory 提出 HTTP 要求 (部分機器翻譯) 的下列各節內容:
下列範例使用 AuthenticationStateProvider 將已驗證使用者的自訂使用者名稱標頭附加至傳出要求。
首先,在 CircuitServicesAccessor
相依性注入 (DI) 文章的下列部分中,實作 Blazor 類別:
從不同的 DI 範圍存取伺服器端 Blazor 服務 (部分機器翻譯)
使用 CircuitServicesAccessor
來在 AuthenticationStateProvider 實作中存取 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);
}
}
在 Program
檔案中,註冊 AuthenticationStateHandler
並將處理常式新增至 IHttpClientFactory 以建立 HttpClient 執行個體:
builder.Services.AddTransient<AuthenticationStateHandler>();
builder.Services.AddHttpClient("HttpMessageHandler")
.AddHttpMessageHandler<AuthenticationStateHandler>();