참고
이 문서의 최신 버전은 아닙니다. 현재 릴리스는 이 문서의 .NET 10 버전을 참조하세요.
이 문서에서는 앱에 토큰을 전달하는 방법을 포함하여 추가 보안 시나리오에 대해 서버 쪽 Blazor 을 구성하는 방법을 설명합니다 Blazor .
참고
이 문서의 코드 예제에서는 NRT(nullable 참조 형식) 및 .NET 컴파일러 null 상태 정적 분석을 채택합니다. 이 분석은 .NET 6 이상의 ASP.NET Core에서 지원됩니다. .NET 5 이하 버전을 대상으로 할 때, 문서의 예제에서 ?, string?, TodoItem[]?, 및 WeatherForecast[]? 형식에 대한 null 유형 지정(IEnumerable<GitHubBranch>?)을 제거하십시오.
서버 쪽 Blazor 앱에 토큰 전달
이 섹션은 Blazor Web App에 적용됩니다. Blazor Server에서 이 문서의 .NET 7 버전 섹션 보기.
액세스 토큰을 사용하여 Blazor Web App를 사용하여 웹 API를 호출하려는 경우 구현을 사용하여 나가는 요청에 사용자의 액세스 토큰을 연결하는 방법을 설명하는 웹 API 호출에 대한 토큰 처리기 사용DelegatingHandler 섹션을 참조하세요. 이 섹션의 다음 지침은 다른 용도로 액세스 토큰, 새로 고침 토큰 및 기타 인증 속성 서버 쪽이 필요한 개발자를 위한 것입니다.
서버 쪽 사용을 위해 토큰 및 기타 인증 속성을 Blazor Web App 에 저장하려면, IHttpContextAccessor/HttpContext (IHttpContextAccessor, HttpContext)을 사용하는 것이 좋습니다. 정적 서버 측 렌더링(정적 SSR) 또는 미리 렌더링 중에 토큰을 가져오는 경우, 대화형 서버 렌더링에 사용할 토큰을 얻기 위해 HttpContext에서 연계 매개 변수를 포함하여 IHttpContextAccessor를 사용하는 것이 지원됩니다. 그러나 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에서처럼 서버 쪽 서비스에 삽입될 수 있습니다. 다음 예제는 외부 웹 API를 호출하기 위해 직접 토큰을 삽입 AuthenticationProcessor 하고 가져올 수 있기 때문에 데모용 또는 서비스에서 특수 처리를 IHttpContextAccessor 수행해야 하는 경우에 해당합니다(웹 API 호출에 직접 사용하는 IHttpContextAccessor 방법에 대한 자세한 내용은 웹 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);
}
}
토큰 처리기가 등록되고 파일에서 명명된 HTTP 클라이언트 Program 에 대한 위임 처리기 역할을 합니다.
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 Core Blazor 인증 및 권한 부여를 참조하세요. 또한, OIDC 공급자를 위한 OIDC(OpenID Connect)를 사용하여 ASP.NET Core Blazor Web App 보안에 설명된 것처럼 유사한 호출 구조를 채택하는 백엔드-프론트엔드(BFF) 패턴을 고려하고, Microsoft Entra ID를 사용하여 Microsoft Entra ID를 사용하여 ASP.NET Core Blazor Web App 를 보호하는 Microsoft 웹의 Entra에 대한 보안을 고려하는 것이 좋습니다.Identity
웹 API 호출에 토큰 처리기 사용
다음 방법은 특히 외부 웹 API 앱에 대한 웹 API 호출을 수행하도록 사용자의 액세스 토큰을 나가는 요청에 연결하기 위한 것입니다. 이 접근 방식은 전역 대화형 서버 렌더링을 채택하는 Blazor Web App에 적용되는 것이며, 전역 대화형 자동 렌더링 모드를 채택하는 Blazor Web App에도 동일한 일반적인 접근 방식이 적용됩니다. 서버에서만 HttpContext을 사용하는 IHttpContextAccessor으로 접근할 수 있다는 것이 바로 유념해야 할 중요한 개념입니다.
이 섹션의 지침에 대한 데모를 보려면 BlazorWebAppOidc 및 BlazorWebAppOidcServer 샘플 앱(.NET 8 이상)을 Blazor 샘플 GitHub 리포지토리에서 참조하세요. 샘플은 Entra 관련 패키지를 사용하지 않고 Microsoft Entra로 전역 대화형 렌더링 모드 및 OIDC 인증을 채택합니다. 샘플은 보안 웹 API를 호출하기 위해 JWT 액세스 토큰을 전달하는 방법을 보여 줍니다.
Microsoft ID 플랫폼은 Identity용 Microsoft 웹 패키지와 함께 자동 토큰 관리 및 갱신을 통해 Blazor Web App에서 웹 API를 호출할 수 있는 API를 제공합니다. 자세한 내용은 Blazor Web App 샘플 GitHub 리포지토리에 있는 BlazorWebAppEntra과 BlazorWebAppEntraBff 및 Blazor 샘플 앱(.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} 자리 표시자는 웹 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}"]와 함께 제공할 수 있으며, {CONFIGURATION KEY} 자리 표시자는 구성 키입니다.
new Uri(builder.Configuration["ExternalApiUri"] ?? throw new IOException("No URI!"))
appsettings.json에서 ExternalApiUri를 지정하세요. 다음 예제에서는 값을 외부 웹 API의 localhost 주소로 https://localhost:7277설정합니다.
"ExternalApiUri": "https://localhost:7277"
이 시점에서 구성 요소가 만든 HttpClient는 보안 웹 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에 대해 계획되어 있으며, 이러한 기능은 나가는 요청 미들웨어(AuthenticationStateProvider#52379)에서 Access dotnet/aspnetcore에 의해 추적됩니다.
대화형 서버 모드(dotnet/aspnetcore #52390)에서 HttpClient에 액세스 토큰을 제공하는 문제는 고급 사용 사례에 대한 유용한 토론 및 잠재적 해결 전략을 포함하는 닫힌 문제입니다.
서버 쪽 Razor 앱의 Blazor 구성 요소 외부에서 사용할 수 있는 토큰은 이 섹션에 설명된 방법을 사용하여 구성 요소에 전달할 수 있습니다. 이 섹션의 예제에서는 액세스, 새로 고침 및 XSRF(요청 방지 위조) 토큰 을 앱에 전달하는 데 Blazor 중점을 두지만 이 방법은 다른 HTTP 컨텍스트 상태에 유효합니다.
참고
구성 요소가 Razor 또는 유효성 검사가 필요한 다른 엔드포인트에 POST할 때, Identity 구성 요소로 XSRF 토큰을 전달하는 것이 유용합니다. 앱에 액세스 및 새로 고침 토큰만 필요한 경우 다음 예제에서 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} 자리 표시자에 명시됩니다.
DI(종속성 주입)에서 토큰을 해결하기 위해 앱 내에서 사용할 수 있는 범위가 지정된 토큰 공급자 서비스를 정의합니다.
TokenProvider.cs:
public class TokenProvider
{
public string? AccessToken { get; set; }
public string? RefreshToken { get; set; }
public string? XsrfToken { get; set; }
}
파일 Program에 다음 서비스를 추가합니다.
-
IHttpClientFactory: 액세스 토큰을 사용하여
WeatherForecastService서버 API에서 날씨 데이터를 가져오는 클래스에서 사용됩니다. -
TokenProvider: 액세스 및 새로 고침 토큰을 보유합니다.
builder.Services.AddHttpClient();
builder.Services.AddScoped<TokenProvider>();
Startup.ConfigureServices
Startup.cs에서 다음을 위한 서비스를 추가합니다.
-
IHttpClientFactory: 액세스 토큰을 사용하여
WeatherForecastService서버 API에서 날씨 데이터를 가져오는 클래스에서 사용됩니다. -
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 패키지용 앱에 패키지 참조를 추가합니다.
참고
.NET 앱에 패키지를 추가하는 방법에 대한 지침은 패키지 사용 워크플로에서 패키지 설치 및 관리의 문서(NuGet 설명서)를 참조하세요. NuGet.org에서 올바른 패키지 버전을 확인합니다.
보안 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 를 명시적으로 설정할 수 있습니다. 다음 예에서는 OIDC(OpenID Connect) 체계를 설정합니다.
여러 인증 미들웨어를 사용하여 인증 체계가 두 개 이상인 앱의 경우 Blazor가 사용하는 체계를 Startup.cs의 엔드포인트 구성에서 명시적으로 설정할 수 있습니다. 다음 예에서는 OIDC(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
});
여러 인증 미들웨어를 사용하여 인증 체계가 두 개 이상인 앱의 경우 Blazor가 사용하는 체계를 Startup.Configure의 엔드포인트 구성에서 명시적으로 설정할 수 있습니다. 다음 예제에서는 Microsoft Entra ID 체계를 설정합니다.
endpoints.MapBlazorHub().RequireAuthorization(
new AuthorizeAttribute
{
AuthenticationSchemes = AzureADDefaults.AuthenticationScheme
});
OIDC(OpenID Connect) v2.0 엔드포인트 사용
.NET 5 이전의 ASP.NET Core 버전에서 인증 라이브러리 및 Blazor 템플릿은 OIDC(OpenID Connect) 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",
...
}
}
ME ID가 아닌 공급자와 같이 앱의 OIDC 공급자에 대해 인증 기관에 세그먼트를 덧붙이는 것이 적절하지 않은 경우, 속성을 직접 설정합니다 Authority. OpenIdConnectOptions 또는 앱 설정 파일에서 Authority 키를 사용하여 속성을 설정합니다.
코드 변경
ID 토큰의 클레임 목록은 v2.0 엔드포인트의 경우 변경됩니다. 변경 내용에 대한 Microsoft 설명서는 사용 중지되었지만 ID 토큰의 클레임에 대한 지침은 ID 토큰 클레임 참조에서 사용할 수 있습니다.
v2.0 엔드포인트의 경우 범위 URI에 리소스가 지정되어 있으므로 OpenIdConnectOptions.Resource에서 OpenIdConnectOptions 속성 설정을 제거합니다.
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options => { ... options.Resource = "..."; // REMOVE THIS LINE ... }
앱 ID URI
- v2.0 엔드포인트를 사용할 경우 API는 API의 고유 식별자를 나타내는
App ID URI를 정의합니다. - 모든 범위에는 앱 ID URI가 접두어로 포함되며 v2.0 엔드포인트는 앱 ID URI를 대상으로 하는 액세스 토큰을 내보냅니다.
- V2.0 엔드포인트를 사용할 경우 서버 API에 구성된 클라이언트 ID가 API 애플리케이션 ID(클라이언트 ID)에서 앱 ID URI로 변경됩니다.
appsettings.json:
{
"AzureAd": {
...
"ClientId": "https://{TENANT}.onmicrosoft.com/{PROJECT NAME}"
...
}
}
OIDC 공급자 앱 등록 설명에서 사용해야 하는 앱 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에서 Startup.cs를 호출하기 바로 전에 미들웨어를 호출합니다.
app.UseMiddleware<UserServiceMiddleware>();
요청 미들웨어에서 나가는 AuthenticationStateProvider에 액세스
AuthenticationStateProvider로부터 DelegatingHandler로 만들어진 HttpClient에 대한 IHttpClientFactory는 나가는 요청 미들웨어에서 회로 작업 처리기를 사용하여 액세스할 수 있습니다.
참고
ASP.NET Core 앱에서 사용하여 HttpClient 만든 인스턴스에서 IHttpClientFactory HTTP 요청에 대한 위임 처리기를 정의하는 일반적인 지침은 ASP.NET Core에서 IHttpClientFactory를 사용하여 HTTP 요청 만들기의 다음 섹션을 참조하세요.
다음 예제에서는 인증된 사용자의 사용자 지정 사용자 이름 헤더를 나가는 요청에 연결하는 데 사용합니다 AuthenticationStateProvider .
먼저 CircuitServicesAccessor 클래스를 DI(종속성 주입) 문서의 다음 섹션에 구현하십시오.
다른 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>();
ASP.NET Core