Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Note
Это не последняя версия этой статьи. Текущий выпуск можно найти в версии этой статьи о .NET 10.
Warning
Эта версия ASP.NET Core больше не поддерживается. Для получения дополнительной информации см. Политику поддержки .NET и .NET Core. Текущий выпуск можно найти в версии этой статьи о .NET 10.
В этой статье описываются дополнительные сценарии безопасности для приложений Blazor WebAssembly.
Присоединение маркеров к исходящим запросам
AuthorizationMessageHandler — это DelegatingHandler для обработки токенов доступа. Токены получаются с помощью службы IAccessTokenProvider, которая регистрируется платформой. Если маркер получить невозможно, выбрасывается исключение AccessTokenNotAvailableException. Класс AccessTokenNotAvailableException имеет метод Redirect для перехода по AccessTokenResult.InteractiveRequestUrl с использованием заданного AccessTokenResult.InteractionOptions с целью разрешить обновление токена доступа.
Для удобства платформа предоставляет BaseAddressAuthorizationMessageHandler предварительно настроенным с базовым адресом приложения как разрешенным URL-адресом. Маркеры доступа добавляются, только если URI запроса находится в базовом URI приложения. Если URI исходящих запросов не находятся в базовом URI приложения, используйте настраиваемый класс AuthorizationMessageHandler (рекомендуется) или настройте AuthorizationMessageHandler.
Note
Помимо настройки клиентского приложения для доступа к API сервера, серверный API должен также разрешить запросы независимо от источника (CORS), если клиент и сервер не находятся по одному и тому же базовому адресу. Дополнительные сведения о конфигурации CORS на стороне сервера см. в разделе "Общий доступ к ресурсам между источниками (CORS)" ниже в этой статье.
В следующем примере :
AddHttpClient добавляет IHttpClientFactory и связанные службы в коллекцию служб и настраивает именованный HttpClient (
WebAPI). HttpClient.BaseAddress — это базовый адрес URI ресурса при отправке запросов. IHttpClientFactory предоставляется пакетом NuGetMicrosoft.Extensions.Http.BaseAddressAuthorizationMessageHandler — это DelegatingHandler, используемый для обработки токенов доступа. Маркеры доступа добавляются, только если URI запроса находится в базовом URI приложения. Дополнительные сведения о экземплярах DelegatingHandler см. в разделе HTTP-запросы с IHttpClientFactory — ASP.NET Core.
IHttpClientFactory.CreateClient создает и настраивает экземпляр HttpClient для исходящих запросов с использованием конфигурации, соответствующей именованному HttpClient (
WebAPI).
В следующем примере HttpClientFactoryServiceCollectionExtensions.AddHttpClient является расширением в Microsoft.Extensions.Http. Добавьте пакет в приложение, которое еще не ссылается на него.
Note
Рекомендации по добавлению пакетов в приложения .NET можно найти в статьях раздела Установка и управление пакетами на странице Потребление пакетов (документация NuGet). Проверьте правильность версий пакета на сайте NuGet.org.
using System.Net.Http;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
...
builder.Services.AddHttpClient("WebAPI",
client => client.BaseAddress = new Uri("https://api.contoso.com/v1.0"))
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>()
.CreateClient("WebAPI"));
Для размещенного Blazorрешения на основе Blazor WebAssembly шаблона проекта, URI запросов находятся в базовом URI приложения. Таким образом, IWebAssemblyHostEnvironment.BaseAddress (new Uri(builder.HostEnvironment.BaseAddress)) присваивается HttpClient.BaseAddress в приложении, созданном из шаблона проекта.
Настроенный объект HttpClient используется для выполнения авторизованных запросов с помощью шаблона try-catch:
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@inject HttpClient Http
...
protected override async Task OnInitializedAsync()
{
try
{
var examples =
await Http.GetFromJsonAsync<ExampleType[]>("ExampleAPIMethod");
...
}
catch (AccessTokenNotAvailableException exception)
{
exception.Redirect();
}
}
Сценарии пользовательских запросов проверки подлинности
В следующих сценариях показано, как настроить запросы проверки подлинности и как получить путь входа из параметров проверки подлинности.
Настройка процесса входа
Управлять дополнительными параметрами в запросе на вход с применением следующих методов один или несколько раз на новом экземпляре InteractiveRequestOptions:
В следующем LoginDisplay примере компонента в запрос входа добавляются дополнительные параметры:
-
promptимеет значениеlogin: заставляет пользователя ввести свои учетные данные для этого запроса, отрицая единый вход. -
loginHintимеет значениеpeter@contoso.com: предварительно заполняет поле имени пользователя или адреса электронной почты страницы входа для пользователяpeter@contoso.com. Приложения часто используют этот параметр во время повторной проверки подлинности, уже извлекая имя пользователя из предыдущего входа с помощьюpreferred_usernameутверждения.
Shared/LoginDisplay.razor:
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@inject NavigationManager Navigation
<AuthorizeView>
<Authorized>
Hello, @context.User.Identity?.Name!
<button @onclick="BeginLogOut">Log out</button>
</Authorized>
<NotAuthorized>
<button @onclick="BeginLogIn">Log in</button>
</NotAuthorized>
</AuthorizeView>
@code{
public void BeginLogOut()
{
Navigation.NavigateToLogout("authentication/logout");
}
public void BeginLogIn()
{
InteractiveRequestOptions requestOptions =
new()
{
Interaction = InteractionType.SignIn,
ReturnUrl = Navigation.Uri,
};
requestOptions.TryAddAdditionalParameter("prompt", "login");
requestOptions.TryAddAdditionalParameter("loginHint", "peter@contoso.com");
Navigation.NavigateToLogin("authentication/login", requestOptions);
}
}
Дополнительные сведения см. на следующих ресурсах:
Настройте параметры перед интерактивным получением токена
Если возникает AccessTokenNotAvailableException, управляйте дополнительными параметрами для нового запроса токена доступа провайдера удостоверений, используя следующие методы один или несколько раз в новом экземпляре InteractiveRequestOptions.
В следующем примере, который получает данные JSON через веб-API, в запрос перенаправления добавляются дополнительные параметры, если токен доступа отсутствует (выбрасывается AccessTokenNotAvailableException):
-
promptимеет значениеlogin: заставляет пользователя ввести свои учетные данные для этого запроса, отрицая единый вход. -
loginHintимеет значениеpeter@contoso.com: предварительно заполняет поле имени пользователя или адреса электронной почты страницы входа для пользователяpeter@contoso.com. Приложения часто используют этот параметр во время повторной проверки подлинности, уже извлекая имя пользователя из предыдущего входа с помощьюpreferred_usernameутверждения.
try
{
var examples = await Http.GetFromJsonAsync<ExampleType[]>("ExampleAPIMethod");
...
}
catch (AccessTokenNotAvailableException ex)
{
ex.Redirect(requestOptions => {
requestOptions.TryAddAdditionalParameter("prompt", "login");
requestOptions.TryAddAdditionalParameter("loginHint", "peter@contoso.com");
});
}
В предыдущем примере предполагается следующее:
- Наличие инструкции
@using/usingдля API в пространстве имен Microsoft.AspNetCore.Components.WebAssembly.Authentication. -
HttpClientвводится какHttp.
Дополнительные сведения см. на следующих ресурсах:
Настройка параметров при использовании IAccessTokenProvider
Если получение токена завершается ошибкой при использовании IAccessTokenProvider, с помощью следующих методов управляйте дополнительными параметрами для нового запроса токена доступа поставщика удостоверений один или несколько раз в новом экземпляре InteractiveRequestOptions.
В следующем примере, который пытается получить токен доступа для пользователя, при вызове TryGetToken, если попытка получить токен завершается ошибкой, в запрос входа добавляются дополнительные параметры.
-
promptимеет значениеlogin: заставляет пользователя ввести свои учетные данные для этого запроса, отрицая единый вход. -
loginHintимеет значениеpeter@contoso.com: предварительно заполняет поле имени пользователя или адреса электронной почты страницы входа для пользователяpeter@contoso.com. Приложения часто используют этот параметр во время повторной проверки подлинности, уже извлекая имя пользователя из предыдущего входа с помощьюpreferred_usernameутверждения.
var tokenResult = await TokenProvider.RequestAccessToken(
new AccessTokenRequestOptions
{
Scopes = [ ... ]
});
if (!tokenResult.TryGetToken(out var token))
{
tokenResult.InteractionOptions.TryAddAdditionalParameter("prompt", "login");
tokenResult.InteractionOptions.TryAddAdditionalParameter("loginHint",
"peter@contoso.com");
Navigation.NavigateToLogin(accessTokenResult.InteractiveRequestUrl,
accessTokenResult.InteractionOptions);
}
var tokenResult = await TokenProvider.RequestAccessToken(
new AccessTokenRequestOptions
{
Scopes = new[] { ... }
});
if (!tokenResult.TryGetToken(out var token))
{
tokenResult.InteractionOptions.TryAddAdditionalParameter("prompt", "login");
tokenResult.InteractionOptions.TryAddAdditionalParameter("loginHint",
"peter@contoso.com");
Navigation.NavigateToLogin(accessTokenResult.InteractiveRequestUrl,
accessTokenResult.InteractionOptions);
}
В предыдущем примере предполагается следующее:
- Наличие инструкции
@using/usingдля API в пространстве имен Microsoft.AspNetCore.Components.WebAssembly.Authentication. -
IAccessTokenProvider вводится как
TokenProvider.
Дополнительные сведения см. на следующих ресурсах:
Выход с настраиваемым URL-адресом возврата
В следующем примере пользователь выходит из системы и возвращается в конечную точку /goodbye:
Navigation.NavigateToLogout("authentication/logout", "goodbye");
Получение пути входа из параметров проверки подлинности
Получите настроенный путь входа из RemoteAuthenticationOptions:
var loginPath =
RemoteAuthOptions.Get(Options.DefaultName).AuthenticationPaths.LogInPath;
В предыдущем примере предполагается следующее:
- Наличие инструкции
@using/usingдля API в следующих пространствах имен: -
IOptionsSnapshot<RemoteAuthenticationOptions<ApiAuthorizationProviderOptions>>вводится какRemoteAuthOptions.
Пользовательский класс AuthorizationMessageHandler
Руководство в этом разделе рекомендуется для клиентских приложений, которые делают исходящие запросы к URI, которые не находятся в базовом URI приложения.
В следующем примере пользовательский класс расширяет AuthorizationMessageHandler для использования в качестве DelegatingHandler для HttpClient. ConfigureHandler настраивает этот обработчик для авторизации исходящих HTTP-запросов с помощью маркера доступа. Маркер доступа прикрепляется, только если по крайней мере один из разрешенных URL-адресов является базовым для URI запроса (HttpRequestMessage.RequestUri).
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
public class CustomAuthorizationMessageHandler : AuthorizationMessageHandler
{
public CustomAuthorizationMessageHandler(IAccessTokenProvider provider,
NavigationManager navigation)
: base(provider, navigation)
{
ConfigureHandler(
authorizedUrls: [ "https://api.contoso.com/v1.0" ],
scopes: [ "example.read", "example.write" ]);
}
}
Note
В этом разделе предыдущий обработчик сообщений используется при создании настроенного HttpClient из внедренного IHttpClientFactory. Если вы не используете объектIHttpClientFactory, необходимо создать HttpClientHandler экземпляр и назначить его AuthorizationMessageHandlerследующим:DelegatingHandler.InnerHandler
InnerHandler = new HttpClientHandler();
Если вы используете InnerHandler, вам не нужно выполнять предыдущее присваивание IHttpClientFactory, как показано в вызове ExampleAPIMethod далее в этом разделе.
В приведенном выше коде области example.read и example.write являются общими примерами, не предназначенными для отражения допустимых областей для конкретного поставщика.
Program В файле CustomAuthorizationMessageHandler регистрируется в качестве временного сервиса и настраивается в качестве DelegatingHandler исходящих HttpResponseMessage экземпляров, сделанных именованным HttpClient.
В следующем примере HttpClientFactoryServiceCollectionExtensions.AddHttpClient является расширением в Microsoft.Extensions.Http. Добавьте пакет в приложение, которое еще не ссылается на него.
Note
Рекомендации по добавлению пакетов в приложения .NET можно найти в статьях раздела Установка и управление пакетами на странице Потребление пакетов (документация NuGet). Проверьте правильность версий пакета на сайте NuGet.org.
builder.Services.AddTransient<CustomAuthorizationMessageHandler>();
builder.Services.AddHttpClient("WebAPI",
client => client.BaseAddress = new Uri("https://api.contoso.com/v1.0"))
.AddHttpMessageHandler<CustomAuthorizationMessageHandler>();
Note
В предыдущем примере CustomAuthorizationMessageHandlerDelegatingHandler регистрируется как временная служба для AddHttpMessageHandler. Временную регистрацию рекомендуется использовать для IHttpClientFactory, который управляет собственными областями решения задач DI. Дополнительные сведения см. на следующих ресурсах:
Для размещенного Blazor решения наBlazor WebAssemblyоснове шаблонаIWebAssemblyHostEnvironment.BaseAddress проекта (new Uri(builder.HostEnvironment.BaseAddress)) назначается объекту HttpClient.BaseAddress.
Настроенный объект HttpClient используется для выполнения авторизованных запросов с помощью шаблона try-catch. Когда клиент создается с использованием пакета CreateClient (Microsoft.Extensions.Http пакет), предоставляются экземпляры HttpClient, которые включают маркеры доступа при выполнении запросов к API сервера. Если URI запроса является относительным URI, как показано в следующем примере (ExampleAPIMethod), он объединяется с BaseAddress, когда клиентское приложение выполняет запрос.
@inject IHttpClientFactory ClientFactory
...
@code {
protected override async Task OnInitializedAsync()
{
try
{
var client = ClientFactory.CreateClient("WebAPI");
var examples =
await client.GetFromJsonAsync<ExampleType[]>("ExampleAPIMethod");
...
}
catch (AccessTokenNotAvailableException exception)
{
exception.Redirect();
}
}
}
Настройка AuthorizationMessageHandler
Для AuthorizationMessageHandler можно настроить разрешенные URL-адреса, области и URL-адреса возврата с помощью метода ConfigureHandler. ConfigureHandler настраивает обработчик для авторизации исходящих HTTP-запросов с помощью маркера доступа. Маркер доступа прикрепляется, только если по крайней мере один из разрешенных URL-адресов является базовым для URI запроса (HttpRequestMessage.RequestUri). Если URI запроса является относительным URI, он объединяется с BaseAddress.
В следующем примере AuthorizationMessageHandler настраивает HttpClient в файле Program.
using System.Net.Http;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
...
builder.Services.AddScoped(sp => new HttpClient(
sp.GetRequiredService<AuthorizationMessageHandler>()
.ConfigureHandler(
authorizedUrls: [ "https://api.contoso.com/v1.0" ],
scopes: [ "example.read", "example.write" ])
.InnerHandler = new HttpClientHandler())
{
BaseAddress = new Uri("https://api.contoso.com/v1.0")
});
В предыдущем коде:
- Области
example.readиexample.writeявляются универсальными примерами и не предназначены для того, чтобы отражать допустимые области для какого-либо конкретного поставщика. - IHttpClientFactory не используется для создания HttpClient экземпляров, поэтому HttpClientHandler экземпляр создается вручную и назначается AuthorizationMessageHandlerему DelegatingHandler.InnerHandler.
Для размещенного Blazor решения на основе Blazor WebAssembly проекта, следующее назначается для IWebAssemblyHostEnvironment.BaseAddress:
- свойству HttpClient.BaseAddress (
new Uri(builder.HostEnvironment.BaseAddress)); - URL-адрес массива
authorizedUrls.
С заданным типом HttpClient
Можно определить типизированный клиент, который будет обрабатывать все аспекты, связанные с HTTP и получением маркеров, в пределах одного класса.
WeatherForecastClient.cs:
using System.Net.Http.Json;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using static {PACKAGE ID/ASSEMBLY NAME}.Data;
public class WeatherForecastClient(HttpClient http)
{
private WeatherForecast[]? forecasts;
public async Task<WeatherForecast[]> GetForecastAsync()
{
try
{
forecasts = await http.GetFromJsonAsync<WeatherForecast[]>(
"WeatherForecast");
}
catch (AccessTokenNotAvailableException exception)
{
exception.Redirect();
}
return forecasts ?? Array.Empty<WeatherForecast>();
}
}
В предыдущем примере WeatherForecast тип представляет собой статический класс, содержащий данные прогноза погоды. Заполнитель {PACKAGE ID/ASSEMBLY NAME} — это идентификатор пакета проекта (<PackageId> в файле проекта) для библиотеки или имени сборки для приложения (например, using static BlazorSample.Data;).
В следующем примере HttpClientFactoryServiceCollectionExtensions.AddHttpClient является расширением в Microsoft.Extensions.Http. Добавьте пакет в приложение, которое еще не ссылается на него.
Note
Рекомендации по добавлению пакетов в приложения .NET можно найти в статьях раздела Установка и управление пакетами на странице Потребление пакетов (документация NuGet). Проверьте правильность версий пакета на сайте NuGet.org.
В файле Program:
using System.Net.Http;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
...
builder.Services.AddHttpClient<WeatherForecastClient>(
client => client.BaseAddress = new Uri("https://api.contoso.com/v1.0"))
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
Для размещенного Blazor решения наBlazor WebAssemblyоснове шаблонаIWebAssemblyHostEnvironment.BaseAddress проекта (new Uri(builder.HostEnvironment.BaseAddress)) назначается объекту HttpClient.BaseAddress.
В компоненте, который извлекает данные о погоде:
@inject WeatherForecastClient Client
...
protected override async Task OnInitializedAsync()
{
forecasts = await Client.GetForecastAsync();
}
Настройка обработчика HttpClient
Обработчик можно дополнительно настроить с помощью ConfigureHandler для исходящих HTTP-запросов.
В следующем примере HttpClientFactoryServiceCollectionExtensions.AddHttpClient является расширением в Microsoft.Extensions.Http. Добавьте пакет в приложение, которое еще не ссылается на него.
Note
Рекомендации по добавлению пакетов в приложения .NET можно найти в статьях раздела Установка и управление пакетами на странице Потребление пакетов (документация NuGet). Проверьте правильность версий пакета на сайте NuGet.org.
В файле Program:
builder.Services.AddHttpClient<WeatherForecastClient>(
client => client.BaseAddress = new Uri("https://api.contoso.com/v1.0"))
.AddHttpMessageHandler(sp => sp.GetRequiredService<AuthorizationMessageHandler>()
.ConfigureHandler(
authorizedUrls: [ "https://api.contoso.com/v1.0" ],
scopes: [ "example.read", "example.write" ]));
builder.Services.AddHttpClient<WeatherForecastClient>(
client => client.BaseAddress = new Uri("https://api.contoso.com/v1.0"))
.AddHttpMessageHandler(sp => sp.GetRequiredService<AuthorizationMessageHandler>()
.ConfigureHandler(
authorizedUrls: new[] { "https://api.contoso.com/v1.0" },
scopes: new[] { "example.read", "example.write" }));
В приведенном выше коде области example.read и example.write являются общими примерами, не предназначенными для отражения допустимых областей для конкретного поставщика.
Для размещенного Blazor решения на основе Blazor WebAssembly проекта, следующее назначается для IWebAssemblyHostEnvironment.BaseAddress:
- свойству HttpClient.BaseAddress (
new Uri(builder.HostEnvironment.BaseAddress)); - URL-адрес массива
authorizedUrls.
Запросы веб-API, не прошедшие проверку подлинности или неавторизованные, в приложении с защищенным клиентом по умолчанию
Приложение, которое обычно использует безопасное значение по умолчанию HttpClient , также может выполнять неавторизованные или несанкционированные запросы веб-API, настроив именованный HttpClient.
В следующем примере HttpClientFactoryServiceCollectionExtensions.AddHttpClient является расширением в Microsoft.Extensions.Http. Добавьте пакет в приложение, которое еще не ссылается на него.
Note
Рекомендации по добавлению пакетов в приложения .NET можно найти в статьях раздела Установка и управление пакетами на странице Потребление пакетов (документация NuGet). Проверьте правильность версий пакета на сайте NuGet.org.
В файле Program:
builder.Services.AddHttpClient("WebAPI.NoAuthenticationClient",
client => client.BaseAddress = new Uri("https://api.contoso.com/v1.0"));
Для размещенного Blazor решения наBlazor WebAssemblyоснове шаблонаIWebAssemblyHostEnvironment.BaseAddress проекта (new Uri(builder.HostEnvironment.BaseAddress)) назначается объекту HttpClient.BaseAddress.
Предшествующая регистрация является дополнением к существующей защищенной регистрации по умолчанию HttpClient.
Компонент создает HttpClient из пакета IHttpClientFactory (Microsoft.Extensions.Http package), чтобы выполнить неавторизованные или несанкционированные запросы:
@inject IHttpClientFactory ClientFactory
...
@code {
protected override async Task OnInitializedAsync()
{
var client = ClientFactory.CreateClient("WebAPI.NoAuthenticationClient");
var examples = await client.GetFromJsonAsync<ExampleType[]>(
"ExampleNoAuthentication");
...
}
}
Note
Контроллер в интерфейсе API сервера (ExampleNoAuthenticationController в предыдущем примере) не помечен атрибутом [Authorize].
Решение о том, следует ли использовать безопасный клиент или небезопасный клиент в качестве экземпляра HttpClient по умолчанию, принимает разработчик. При принятии такого решения рекомендуется учесть соотношение неавторизованных и аутентифицированных конечных точек, к которым обращается приложение. Если приложение отправляет большинство запросов к безопасным конечным точкам API, используйте аутентифицированный экземпляр HttpClient в качестве значения по умолчанию. В противном случае в качестве значения по умолчанию зарегистрируйте неаутентифицированный экземпляр HttpClient.
Альтернативный подход к использованию IHttpClientFactory заключается в создании типизированного клиента для неаутентифицированного доступа к анонимным конечным точкам.
Запрос дополнительных токенов доступа
Токены доступа можно получать самостоятельно, вызывая IAccessTokenProvider.RequestAccessToken. В приведенном ниже примере приложению требуется дополнительный доступ для настройки по умолчанию HttpClient. Пример Microsoft Authentication Library (MSAL) настраивает область с помощью MsalProviderOptions:
В файле Program:
builder.Services.AddMsalAuthentication(options =>
{
...
options.ProviderOptions.AdditionalScopesToConsent.Add("{CUSTOM SCOPE 1}");
options.ProviderOptions.AdditionalScopesToConsent.Add("{CUSTOM SCOPE 2}");
}
Заполнители {CUSTOM SCOPE 1} и {CUSTOM SCOPE 2} в предыдущем примере — это пользовательские области.
Note
AdditionalScopesToConsent не может подготавливать делегированные разрешения пользователей для Microsoft Graph через пользовательский интерфейс согласия Microsoft Entra ID, когда пользователь сначала использует приложение, зарегистрированное в Microsoft Azure. Дополнительные сведения см. в разделе Use API Graph with ASP.NET Core Blazor WebAssembly.
Метод IAccessTokenProvider.RequestAccessToken предоставляет перегрузку, которая позволяет приложению подготавливать маркер доступа с указанным набором областей.
В компоненте Razor:
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@inject IAccessTokenProvider TokenProvider
...
var tokenResult = await TokenProvider.RequestAccessToken(
new AccessTokenRequestOptions
{
Scopes = [ "{CUSTOM SCOPE 1}", "{CUSTOM SCOPE 2}" ]
});
if (tokenResult.TryGetToken(out var token))
{
...
}
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@inject IAccessTokenProvider TokenProvider
...
var tokenResult = await TokenProvider.RequestAccessToken(
new AccessTokenRequestOptions
{
Scopes = new[] { "{CUSTOM SCOPE 1}", "{CUSTOM SCOPE 2}" }
});
if (tokenResult.TryGetToken(out var token))
{
...
}
Заполнители {CUSTOM SCOPE 1} и {CUSTOM SCOPE 2} в предыдущем примере — это пользовательские области.
AccessTokenResult.TryGetToken возвращает:
-
trueсtokenдля использования. -
false, если токен не извлечён.
Общий доступ к ресурсам между источниками (CORS)
При отправке учетных данных (файлов cookie или заголовков авторизации) в запросах CORS заголовок Authorization должен быть разрешен политикой CORS.
Следующая политика включает в себя настройку для следующих элементов:
- Источники запроса (
http://localhost:5000,https://localhost:5001). - Любой метод (глагол).
- Заголовки
Content-TypeиAuthorization. Чтобы разрешить настраиваемый заголовок (например,x-custom-header), укажите заголовок при вызове WithHeaders. - Учетные данные, установленные клиентским кодом JavaScript (для свойства
credentialsзадано значениеinclude).
app.UseCors(policy =>
policy.WithOrigins("http://localhost:5000", "https://localhost:5001")
.AllowAnyMethod()
.WithHeaders(HeaderNames.ContentType, HeaderNames.Authorization,
"x-custom-header")
.AllowCredentials());
Размещенное решение Blazor на основе шаблона проекта Blazor WebAssembly использует тот же базовый адрес для клиентских и серверных приложений. Клиентское приложение HttpClient.BaseAddress установлено на универсальный код ресурса builder.HostEnvironment.BaseAddress. Конфигурация CORS не требуется в конфигурации по умолчанию размещенного Blazor решения. Дополнительным клиентским приложениям, которые не размещаются в серверном проекте и не используют базовый адрес серверного приложения требуется конфигурация CORS в серверном проекте.
Дополнительные сведения см. в разделе Enable Cross-Origin Requests (CORS) в ASP.NET Core и в компоненте тестировщика HTTP-запросов демонстрационного приложения (Components/HTTPRequestTester.razor).
Обработка ошибок запроса токена
Когда одностраничное приложение (SPA) выполняет аутентификацию пользователя с помощью OpenID Connect (OIDC), состояние аутентификации сохраняется локально в SPA и у поставщика Identity (IP) в виде сеанса cookie, устанавливаемого после того, как пользователь вводит свои учетные данные.
Маркеры, которые IP-адрес выдает для пользователя, обычно действительны недолго (как правило, около часа), поэтому клиентское приложение должно регулярно получать новые маркеры. В противном случае после истечения срока действия предоставленных токенов пользователь будет автоматически выведен из системы. В большинстве случаев клиенты OIDC могут подготавливать новые токены без необходимости повторной аутентификации пользователя благодаря сохранению состояния проверки подлинности или "сеанса" у поставщика удостоверений (IP).
В некоторых случаях клиент не может получить токен без участия пользователя, например, когда пользователь по какой-либо причине явно вышел из системы IP. Этот сценарий возникает, если пользователь посещает https://login.microsoftonline.com и выходит из системы. В этих сценариях приложение не знает сразу, что пользователь выошел из системы. Любой маркер, который хранит клиент, больше не может быть допустимым. Кроме того, клиент не может подготовить новый маркер без участия пользователя после истечения срока действия текущего маркера.
Такие сценарии нехарактерны для проверки подлинности на основе маркеров. Они проистекают из особенностей одностраничных приложений. SPA, использующая файлы cookie, также не вызывает API сервера, если удалена проверка подлинности cookie.
Когда приложение выполняет вызовы API к защищенным ресурсам, необходимо учитывать следующее:
- Для подготовки нового маркера доступа с целью вызова API пользователю может потребоваться пройти проверку подлинности повторно.
- Даже если у клиента есть маркер, который представляется действительным, вызов сервера может завершиться ошибкой из-за того, что маркер был отозван пользователем.
Когда приложение запрашивает маркер, возможны два результата:
- Запрос выполняется успешно, и приложение получает действительный маркер.
- Запрос завершается ошибкой, и приложение должно повторно провести проверку подлинности пользователя, чтобы получить новый маркер.
При сбое запроса токена необходимо решить, хотите ли вы сохранить текущее состояние перед выполнением перенаправления. Существует несколько подходов к хранению состояния в порядке растущей сложности.
- Сохраните текущее состояние страницы в хранилище сеансов. Во время выполнения метода жизненного цикла
OnInitializedAsync(OnInitializedAsync) проверьте, можно ли восстановить состояние, прежде чем продолжать. - Добавьте параметр строки запроса и используйте его, чтобы сигнализировать приложению о необходимости восстановить ранее сохраненное состояние.
- Добавьте параметр строки запроса с уникальным идентификатором для сохранения данных в хранилище сеансов без риска конфликтов с другими элементами.
Сохранение состояния приложения перед операцией проверки подлинности с помощью хранилища сеансов
В приведенном ниже примере показано, как выполнить следующие задачи.
- Сохраните состояние перед перенаправлением на страницу авторизации.
- Восстановите предыдущее состояние после проверки подлинности с помощью параметра строки запроса.
...
@using System.Text.Json
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@inject IAccessTokenProvider TokenProvider
@inject IJSRuntime JS
@inject NavigationManager Navigation
<EditForm Model="User" OnSubmit="OnSaveAsync">
<label>
First Name:
<InputText @bind-Value="User!.Name" />
</label>
<label>
Last Name:
<InputText @bind-Value="User!.LastName" />
</label>
<button type="submit">Save User</button>
</EditForm>
@code {
public Profile User { get; set; } = new Profile();
protected override async Task OnInitializedAsync()
{
var currentQuery = new Uri(Navigation.Uri).Query;
if (currentQuery.Contains("state=resumeSavingProfile"))
{
var user = await JS.InvokeAsync<string>("sessionStorage.getItem",
"resumeSavingProfile");
if (!string.IsNullOrEmpty(user))
{
User = JsonSerializer.Deserialize<Profile>(user);
}
}
}
public async Task OnSaveAsync()
{
var http = new HttpClient();
http.BaseAddress = new Uri(Navigation.BaseUri);
var resumeUri = Navigation.Uri + $"?state=resumeSavingProfile";
var tokenResult = await TokenProvider.RequestAccessToken(
new AccessTokenRequestOptions
{
ReturnUrl = resumeUri
});
if (tokenResult.TryGetToken(out var token))
{
http.DefaultRequestHeaders.Add("Authorization",
$"Bearer {token.Value}");
await http.PostAsJsonAsync("Save", User);
}
else
{
await JS.InvokeVoidAsync("sessionStorage.setItem",
"resumeSavingProfile", JsonSerializer.Serialize(User));
Navigation.NavigateTo(tokenResult.InteractiveRequestUrl);
}
}
public class Profile
{
public string? FirstName { get; set; }
public string? LastName { get; set; }
}
}
Сохранение состояния приложения перед операцией проверки подлинности с хранилищем сеансов и контейнером состояния
Во время операции аутентификации иногда необходимо сохранить состояние приложения перед перенаправлением браузера на IP. Например, такая потребность может возникнуть, когда вы используете контейнер состояния и хотите восстановить состояние после успешной проверки подлинности. Вы можете использовать пользовательский объект состояния проверки подлинности для сохранения состояния приложения или ссылки на него и восстанавливать это состояние после успешного завершения проверки подлинности. Такой подход демонстрируется в приведенном ниже примере.
В приложении создается класс контейнера состояния со свойствами для хранения значений состояния приложения. В следующем примере контейнер используется для поддержания значения счетчика в компоненте шаблона проекта по умолчанию (BlazorCounter). Методы сериализации и десериализации контейнера основаны на System.Text.Json.
using System.Text.Json;
public class StateContainer
{
public int CounterValue { get; set; }
public string GetStateForLocalStorage() => JsonSerializer.Serialize(this);
public void SetStateFromLocalStorage(string locallyStoredState)
{
var deserializedState =
JsonSerializer.Deserialize<StateContainer>(locallyStoredState);
CounterValue = deserializedState.CounterValue;
}
}
Компонент Counter использует контейнер состояния для хранения значения currentCount за пределами компонента:
@page "/counter"
@inject StateContainer State
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
protected override void OnInitialized()
{
if (State.CounterValue > 0)
{
currentCount = State.CounterValue;
}
}
private void IncrementCount()
{
currentCount++;
State.CounterValue = currentCount;
}
}
Создайте ApplicationAuthenticationState на основе RemoteAuthenticationState. Укажите свойство Id, которое служит идентификатором для локально хранящегося состояния.
ApplicationAuthenticationState.cs:
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
public class ApplicationAuthenticationState : RemoteAuthenticationState
{
public string? Id { get; set; }
}
Компонент Authentication (Authentication.razor) сохраняет и восстанавливает состояние приложения, используя локальное хранилище сеансов, с помощью методов сериализации и десериализации StateContainer (GetStateForLocalStorage и SetStateFromLocalStorage):
@page "/authentication/{action}"
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@inject IJSRuntime JS
@inject StateContainer State
<RemoteAuthenticatorViewCore Action="Action"
TAuthenticationState="ApplicationAuthenticationState"
AuthenticationState="AuthenticationState"
OnLogInSucceeded="RestoreState"
OnLogOutSucceeded="RestoreState" />
@code {
[Parameter]
public string? Action { get; set; }
public ApplicationAuthenticationState AuthenticationState { get; set; } =
new ApplicationAuthenticationState();
protected override async Task OnInitializedAsync()
{
if (RemoteAuthenticationActions.IsAction(RemoteAuthenticationActions.LogIn,
Action) ||
RemoteAuthenticationActions.IsAction(RemoteAuthenticationActions.LogOut,
Action))
{
AuthenticationState.Id = Guid.NewGuid().ToString();
await JS.InvokeVoidAsync("sessionStorage.setItem",
AuthenticationState.Id, State.GetStateForLocalStorage());
}
}
private async Task RestoreState(ApplicationAuthenticationState state)
{
if (state.Id != null)
{
var locallyStoredState = await JS.InvokeAsync<string>(
"sessionStorage.getItem", state.Id);
if (locallyStoredState != null)
{
State.SetStateFromLocalStorage(locallyStoredState);
await JS.InvokeVoidAsync("sessionStorage.removeItem", state.Id);
}
}
}
}
В этом примере для проверки подлинности используется Microsoft Entra (ME-ID). В файле Program:
-
ApplicationAuthenticationStateнастроен как тип Microsoft Authentication Library (MSAL)RemoteAuthenticationState. - Контейнер состояния регистрируется в контейнере сервиса.
builder.Services.AddMsalAuthentication<ApplicationAuthenticationState>(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
});
builder.Services.AddSingleton<StateContainer>();
Настройка маршрутов приложения
Библиотека Microsoft.AspNetCore.Components.WebAssembly.Authentication использует маршруты, показанные в следующей таблице, для представления различных состояний проверки подлинности.
| Route | Purpose |
|---|---|
authentication/login |
Активирует операцию входа. |
authentication/login-callback |
Обрабатывает результат любой операции входа. |
authentication/login-failed |
Отображает сообщения об ошибках при сбое операции входа по какой-либо причине. |
authentication/logout |
Инициирует процесс выхода. |
authentication/logout-callback |
Обрабатывает результат операции выхода из системы. |
authentication/logout-failed |
Отображает сообщения об ошибках при сбое операции выхода по какой-либо причине. |
authentication/logged-out |
Указывает, что пользователь успешно выполнил выход. |
authentication/profile |
Активирует операцию изменения профиля пользователя. |
authentication/register |
Активирует операцию регистрации нового пользователя. |
Маршруты, представленные в предыдущей таблице, можно настраивать с помощью RemoteAuthenticationOptions<TRemoteAuthenticationProviderOptions>.AuthenticationPaths. При настройке параметров для предоставления пользовательских маршрутов убедитесь в том, что у приложения есть маршрут для обработки каждого пути.
В следующем примере все пути имеют префикс /security.
Компонент Authentication (Authentication.razor):
@page "/security/{action}"
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
<RemoteAuthenticatorView Action="@Action" />
@code{
[Parameter]
public string? Action { get; set; }
}
@page "/security/{action}"
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
<RemoteAuthenticatorView Action="@Action" />
@code{
[Parameter]
public string Action { get; set; }
}
В файле Program:
builder.Services.AddApiAuthorization(options => {
options.AuthenticationPaths.LogInPath = "security/login";
options.AuthenticationPaths.LogInCallbackPath = "security/login-callback";
options.AuthenticationPaths.LogInFailedPath = "security/login-failed";
options.AuthenticationPaths.LogOutPath = "security/logout";
options.AuthenticationPaths.LogOutCallbackPath = "security/logout-callback";
options.AuthenticationPaths.LogOutFailedPath = "security/logout-failed";
options.AuthenticationPaths.LogOutSucceededPath = "security/logged-out";
options.AuthenticationPaths.ProfilePath = "security/profile";
options.AuthenticationPaths.RegisterPath = "security/register";
});
Если требуются совершенно разные пути, задайте маршруты, как описано выше, и обработайте RemoteAuthenticatorView с помощью явного параметра действия:
@page "/register"
<RemoteAuthenticatorView Action="RemoteAuthenticationActions.Register" />
При необходимости можно разделить пользовательский интерфейс на несколько страниц.
Настройка пользовательского интерфейса проверки подлинности
RemoteAuthenticatorView включает набор фрагментов пользовательского интерфейса по умолчанию для каждого состояния проверки подлинности. Каждое состояние можно настроить, передав пользовательский объект RenderFragment. Чтобы настроить текст, отображаемый во время первоначального входа в систему, можно изменить RemoteAuthenticatorView указанным ниже образом.
Компонент Authentication (Authentication.razor):
@page "/security/{action}"
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
<RemoteAuthenticatorView Action="@Action">
<LoggingIn>
You are about to be redirected to https://login.microsoftonline.com.
</LoggingIn>
</RemoteAuthenticatorView>
@code{
[Parameter]
public string? Action { get; set; }
}
@page "/security/{action}"
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
<RemoteAuthenticatorView Action="@Action">
<LoggingIn>
You are about to be redirected to https://login.microsoftonline.com.
</LoggingIn>
</RemoteAuthenticatorView>
@code{
[Parameter]
public string Action { get; set; }
}
В RemoteAuthenticatorView есть фрагмент, который можно использовать для каждого маршрута проверки подлинности, как показано в приведенной ниже таблице.
| Route | Fragment |
|---|---|
authentication/login |
<LoggingIn> |
authentication/login-callback |
<CompletingLoggingIn> |
authentication/login-failed |
<LogInFailed> |
authentication/logout |
<LogOut> |
authentication/logout-callback |
<CompletingLogOut> |
authentication/logout-failed |
<LogOutFailed> |
authentication/logged-out |
<LogOutSucceeded> |
authentication/profile |
<UserProfile> |
authentication/register |
<Registering> |
Настройка пользователя
Пользователей, привязанных к приложению, можно настраивать.
Настройка пользователя с помощью утверждения полезной нагрузки
В приведенном ниже примере пользователи приложения, прошедшие проверку подлинности, получают утверждение amr для каждого из методов проверки подлинности. Утверждение amr определяет, как субъект токена был аутентифицирован в платформа удостоверений Майкрософт версии 1.0 payload claims. В примере используется настраиваемый класс учетной записи пользователя на основе RemoteUserAccount.
Создайте класс, расширяющий класс RemoteUserAccount. В приведенном ниже примере свойству AuthenticationMethod присваивается пользовательский массив значений свойств JSON amr.
AuthenticationMethod заполняется платформой автоматически при проверке подлинности пользователя.
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
public class CustomUserAccount : RemoteUserAccount
{
[JsonPropertyName("amr")]
public string[]? AuthenticationMethod { get; set; }
}
Создайте фабрику, расширяющую AccountClaimsPrincipalFactory<TAccount> для создания утверждений на основе методов проверки подлинности пользователя, хранящихся в CustomUserAccount.AuthenticationMethod:
using System.Security.Claims;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication.Internal;
public class CustomAccountFactory(NavigationManager navigation,
IAccessTokenProviderAccessor accessor)
: AccountClaimsPrincipalFactory<CustomUserAccount>(accessor)
{
public override async ValueTask<ClaimsPrincipal> CreateUserAsync(
CustomUserAccount account, RemoteAuthenticationUserOptions options)
{
var initialUser = await base.CreateUserAsync(account, options);
if (initialUser.Identity != null && initialUser.Identity.IsAuthenticated)
{
var userIdentity = (ClaimsIdentity)initialUser.Identity;
if (account.AuthenticationMethod is not null)
{
foreach (var value in account.AuthenticationMethod)
{
userIdentity.AddClaim(new Claim("amr", value));
}
}
}
return initialUser;
}
}
Зарегистрируйте CustomAccountFactory для используемого поставщика проверки подлинности. Допустима любая из следующих регистраций:
-
using Microsoft.AspNetCore.Components.WebAssembly.Authentication; ... builder.Services.AddOidcAuthentication<RemoteAuthenticationState, CustomUserAccount>(options => { ... }) .AddAccountClaimsPrincipalFactory<RemoteAuthenticationState, CustomUserAccount, CustomAccountFactory>(); -
using Microsoft.AspNetCore.Components.WebAssembly.Authentication; ... builder.Services.AddMsalAuthentication<RemoteAuthenticationState, CustomUserAccount>(options => { ... }) .AddAccountClaimsPrincipalFactory<RemoteAuthenticationState, CustomUserAccount, CustomAccountFactory>(); -
using Microsoft.AspNetCore.Components.WebAssembly.Authentication; ... builder.Services.AddApiAuthorization<RemoteAuthenticationState, CustomUserAccount>(options => { ... }) .AddAccountClaimsPrincipalFactory<RemoteAuthenticationState, CustomUserAccount, CustomAccountFactory>();
Группы безопасности и роли ME-ID с пользовательским классом учетной записи
Дополнительный пример, который работает с группами безопасности ME-ID, ролями администратора ME-ID и пользовательским классом учетных записей, см. в разделе ASP.NET Core Blazor WebAssembly с группами и ролями Microsoft Entra ID.
Предварительный рендеринг с проверкой подлинности
Предварительная подготовка содержимого, требующего аутентификации и авторизации, сейчас не поддерживается. Выполнив инструкции в одном из разделов, посвященных безопасности приложений Blazor WebAssembly, выполните приведенные ниже действия, чтобы создать приложение, которое:
- предварительно отрисовывает пути, не требующие авторизации;
- Не производит предварительную генерацию маршрутов, для которых требуется авторизация.
Для файла проекта ClientProgram вынесите общие регистрации служб в отдельный метод (например, создайте метод ConfigureCommonServices в проекте Client). Общими называются службы, которые разработчик регистрирует для использования клиентским и серверным проектами.
public static void ConfigureCommonServices(IServiceCollection services)
{
services.Add...;
}
В файле Program:
var builder = WebAssemblyHostBuilder.CreateDefault(args);
...
builder.Services.AddScoped( ... );
ConfigureCommonServices(builder.Services);
await builder.Build().RunAsync();
Server В файле проекта Program зарегистрируйте следующие дополнительные службы и вызовите ConfigureCommonServices:
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Server;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
...
builder.Services.AddRazorPages();
builder.Services.TryAddScoped<AuthenticationStateProvider,
ServerAuthenticationStateProvider>();
Client.Program.ConfigureCommonServices(services);
В методе Server проекта Startup.ConfigureServices зарегистрируйте следующие дополнительные сервисы и вызовите функцию ConfigureCommonServices:
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Server;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
public void ConfigureServices(IServiceCollection services)
{
...
services.AddRazorPages();
services.AddScoped<AuthenticationStateProvider,
ServerAuthenticationStateProvider>();
services.AddScoped<SignOutSessionStateManager>();
Client.Program.ConfigureCommonServices(services);
}
См. раздел Blazor для получения дополнительных сведений о поставщике проверки подлинности сервера ServerAuthenticationStateProvider framework (Blazor).
В файле Server проекта Pages/_Host.cshtml замените вспомогательное приложение тегов Component (<component ... />) на следующее:
<div id="app">
@if (HttpContext.Request.Path.StartsWithSegments("/authentication"))
{
<component type="typeof({CLIENT APP ASSEMBLY NAME}.App)"
render-mode="WebAssembly" />
}
else
{
<component type="typeof({CLIENT APP ASSEMBLY NAME}.App)"
render-mode="WebAssemblyPrerendered" />
}
</div>
В предыдущем примере:
- Заполнитель
{CLIENT APP ASSEMBLY NAME}— это имя сборки клиентского приложения (напримерBlazorSample.Client). - Условная проверка для сегмента пути
/authentication:- Избегает рендеринга (
render-mode="WebAssembly") для путей аутентификации. - Выполняет пререндеринг (
render-mode="WebAssemblyPrerendered") для путей, не относящихся к аутентификации.
- Избегает рендеринга (
Варианты для размещенных приложений и сторонних поставщиков входа
При аутентификации и авторизации размещенного приложения Blazor WebAssembly в стороннем поставщике доступно несколько вариантов аутентификации пользователя. Выбор варианта зависит от вашего сценария.
Дополнительные сведения см. в разделе Persist дополнительные утверждения и маркеры внешних поставщиков в ASP.NET Core.
Проверка подлинности пользователей для вызова только защищенных сторонних API
Проверьте подлинность пользователя с помощью потока OAuth на стороне клиента в стороннем поставщике API:
builder.services.AddOidcAuthentication(options => { ... });
В этом сценарии:
- Сервер, на котором размещено приложение, не имеет особого значения.
- Невозможно обеспечить защиту API на сервере.
- Приложение может вызывать только защищенные сторонние интерфейсы API.
Проверка подлинности пользователей с третьим поставщиком и вызов защищенных API на хост-сервере, а также у третьего поставщика.
Настройте Identity с помощью стороннего поставщика аутентификации. Получите токены, необходимые для доступа к сторонним API, и сохраните их.
При входе в систему пользователем Identity собирает токены доступа и обновления в рамках процесса проверки подлинности. На этом этапе существует несколько подходов для отправки вызовов API к сторонним API.
Использование токена доступа сервера для получения стороннего токена доступа
С помощью созданного на сервере токена доступа получите сторонний токен доступа из конечной точки API сервера. Затем используйте сторонний токен доступа для обращения к ресурсам стороннего API непосредственно на клиенте из Identity.
Мы не рекомендуем этот подход. Этот подход требует лечения стороннего маркера доступа, как если бы он был создан для общедоступного клиента. С точки зрения использования OAuth у общедоступного приложения нет секрета клиента, так как оно не может считаться доверенным и надежно хранить секреты, а токен доступа создается для конфиденциального клиента. Конфиденциальный клиент — это клиент, у которого есть секрет клиента. Кроме того, предполагается, что он способен надежно хранить секреты.
- Исходя из того, что третья сторона выдала токен более доверенному клиенту, сторонним токенам доступа могут быть предоставлены дополнительные области для выполнения конфиденциальных операций.
- Аналогичным образом, токены обновления не должны выдаваться недоверенному клиенту, так как в этом случае клиент получает неограниченный доступ до применения других ограничений.
Отправка вызовов API с API клиента на API сервера для вызова сторонних API
Отправьте вызов API с API клиента на API сервера. На сервере получите токен доступа для ресурса стороннего API и осуществите необходимый вызов.
Мы рекомендуем этот подход. Хотя этот подход требует дополнительного сетевого прыжка через сервер для вызова стороннего API, он в конечном итоге приводит к более безопасному интерфейсу:
- Сервер может хранить токены обновления и гарантировать, что приложение не потеряет доступ к сторонним ресурсам.
- Приложение не может получать с сервера токены доступа, содержащие более конфиденциальные разрешения.
Использование конечных точек OpenID Connect (OIDC) версии 2.0
В библиотеке проверки подлинности и шаблонах проекта Blazor используются конечные точки OpenID Connect (OIDC) версии 1.0. Чтобы использовать конечную точку версии 2.0, настройте параметр JwtBearerOptions.Authority маркера носителя JWT. В следующем примере ME-ID настраивается для версии 2.0 путем добавления v2.0 сегмента к свойству Authority :
using Microsoft.AspNetCore.Authentication.JwtBearer;
...
builder.Services.Configure<JwtBearerOptions>(
JwtBearerDefaults.AuthenticationScheme,
options =>
{
options.Authority += "/v2.0";
});
Кроме того, параметр можно задать в файле параметров приложения (appsettings.json):
{
"Local": {
"Authority": "https://login.microsoftonline.com/common/oauth2/v2.0",
...
}
}
Если добавление сегмента к полномочию не подходит для поставщика OIDC приложения, например, для поставщиков, не относящихся к ME-ID, задайте свойство напрямую Authority. Задайте свойство либо в JwtBearerOptions, либо в файле параметров приложения (appsettings.json) с помощью ключа Authority.
Список утверждений в маркере идентификатора изменяется для конечных точек версии 2.0. Документация Microsoft по изменениям была снята с производства, но руководство по утверждениям в токене идентификатора доступно в справочнике по утверждениям токена идентификатора ID token claims reference.
Настройка и использование gRPC в компонентах
Чтобы настроить приложение Blazor WebAssembly для использования платформы ASP.NET Core gRPC:
- Включите gRPC-Web на сервере. Дополнительные сведения см. в разделе gRPC-Web в приложениях gRPC на платформе ASP.NET Core.
- Зарегистрируйте службы gRPC для обработчика сообщений приложения. В следующем примере настраивается обработчик сообщений авторизации приложения для использования
GreeterClientслужбы из руководства по gRPC (вProgramфайле).
Note
Предварительная отрисовка включена по умолчанию в Blazor Web Apps, поэтому необходимо сначала учитывать отрисовку компонента с сервера, а затем от клиента. Любое предварительно созданное состояние должно передаваться клиенту, чтобы его можно было повторно использовать. Дополнительные сведения см. в разделе ASP.NET Core Blazor сохраняемость предопределенного состояния.
Note
Предварительная отрисовка включена по умолчанию в размещенных Blazor WebAssembly приложениях, поэтому сначала необходимо учитывать отрисовку компонента с сервера, а затем от клиента. Любое предварительно созданное состояние должно передаваться клиенту, чтобы его можно было повторно использовать. Для получения дополнительной информации см. раздел
using System.Net.Http;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Grpc.Net.Client;
using Grpc.Net.Client.Web;
...
builder.Services.AddScoped(sp =>
{
var baseAddressMessageHandler =
sp.GetRequiredService<BaseAddressAuthorizationMessageHandler>();
baseAddressMessageHandler.InnerHandler = new HttpClientHandler();
var grpcWebHandler =
new GrpcWebHandler(GrpcWebMode.GrpcWeb, baseAddressMessageHandler);
var channel = GrpcChannel.ForAddress(builder.HostEnvironment.BaseAddress,
new GrpcChannelOptions { HttpHandler = grpcWebHandler });
return new Greeter.GreeterClient(channel);
});
Компонент в клиентском приложении может выполнять вызовы gRPC с помощью клиента gRPC (Grpc.razor):
@page "/grpc"
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]
@inject Greeter.GreeterClient GreeterClient
<h1>Invoke gRPC service</h1>
<p>
<input @bind="name" placeholder="Type your name" />
<button @onclick="GetGreeting" class="btn btn-primary">Call gRPC service</button>
</p>
Server response: <strong>@serverResponse</strong>
@code {
private string name = "Bert";
private string? serverResponse;
private async Task GetGreeting()
{
try
{
var request = new HelloRequest { Name = name };
var reply = await GreeterClient.SayHelloAsync(request);
serverResponse = reply.Message;
}
catch (Grpc.Core.RpcException ex)
when (ex.Status.DebugException is
AccessTokenNotAvailableException tokenEx)
{
tokenEx.Redirect();
}
}
}
Чтобы применить свойство Status.DebugException, используйте Grpc.Net.Client версии 2.30.0 или более поздней.
Дополнительные сведения см. в разделе gRPC-Web в приложениях gRPC на платформе ASP.NET Core.
Замена реализации AuthenticationService
В следующих подразделах объясняется, как выполнить замену:
- любой реализации
AuthenticationServiceJavaScript. - Библиотека аутентификации Microsoft для ДжаваСкрипт (
MSAL.js).
Замените любую реализацию JavaScript AuthenticationService
Создайте библиотеку JavaScript для обработки сведений о пользовательской проверке подлинности.
Warning
В этом разделе представлены подробные инструкции по реализации класса RemoteAuthenticationService<TRemoteAuthenticationState,TAccount,TProviderOptions> по умолчанию. Код TypeScript в этом разделе применяется специально к ASP.NET Core в .NET 7 и подлежит изменению без уведомления в предстоящих выпусках ASP.NET Core.
// .NET makes calls to an AuthenticationService object in the Window.
declare global {
interface Window { AuthenticationService: AuthenticationService }
}
export interface AuthenticationService {
// Init is called to initialize the AuthenticationService.
public static init(settings: UserManagerSettings & AuthorizeServiceSettings, logger: any) : Promise<void>;
// Gets the currently authenticated user.
public static getUser() : Promise<{[key: string] : string }>;
// Tries to get an access token silently.
public static getAccessToken(options: AccessTokenRequestOptions) : Promise<AccessTokenResult>;
// Tries to sign in the user or get an access token interactively.
public static signIn(context: AuthenticationContext) : Promise<AuthenticationResult>;
// Handles the sign-in process when a redirect is used.
public static async completeSignIn(url: string) : Promise<AuthenticationResult>;
// Signs the user out.
public static signOut(context: AuthenticationContext) : Promise<AuthenticationResult>;
// Handles the signout callback when a redirect is used.
public static async completeSignOut(url: string) : Promise<AuthenticationResult>;
}
// The rest of these interfaces match their C# definitions.
export interface AccessTokenRequestOptions {
scopes: string[];
returnUrl: string;
}
export interface AccessTokenResult {
status: AccessTokenResultStatus;
token?: AccessToken;
}
export interface AccessToken {
value: string;
expires: Date;
grantedScopes: string[];
}
export enum AccessTokenResultStatus {
Success = 'Success',
RequiresRedirect = 'RequiresRedirect'
}
export enum AuthenticationResultStatus {
Redirect = 'Redirect',
Success = 'Success',
Failure = 'Failure',
OperationCompleted = 'OperationCompleted'
};
export interface AuthenticationResult {
status: AuthenticationResultStatus;
state?: unknown;
message?: string;
}
export interface AuthenticationContext {
state?: unknown;
interactiveRequest: InteractiveAuthenticationRequest;
}
export interface InteractiveAuthenticationRequest {
scopes?: string[];
additionalRequestParameters?: { [key: string]: any };
};
Вы можете импортировать библиотеку, удалив исходный тег <script> и добавив тег <script>, который загружает пользовательскую библиотеку. В следующем примере показано, как заменить тег <script> по умолчанию на тег, который загружает библиотеку с именем CustomAuthenticationService.js из папки wwwroot/js.
Перед wwwroot/index.html скриптом (Blazor) внутри закрывающего _framework/blazor.webassembly.js тега</body>:
- <script src="_content/Microsoft.Authentication.WebAssembly.Msal/AuthenticationService.js"></script>
+ <script src="js/CustomAuthenticationService.js"></script>
Дополнительные сведения см. в разделе AuthenticationService.ts в репозитории dotnet/aspnetcore GitHub .
Note
Ссылки на справочную документацию .NET обычно ведут на ветвь репозитория по умолчанию, которая представляет собой текущую разработку следующего релиза .NET. Чтобы выбрать тег для определенного выпуска, используйте раскрывающийся список Switch branches or tags (Переключение ветвей или тегов). Дополнительные сведения см. в разделе Как выбрать тег версии исходного кода ASP.NET Core (dotnet/AspNetCore.Docs #26205).
Замените Microsoft Authentication Library for JavaScript (MSAL.js)
Если приложению требуется пользовательская версия Microsoft Authentication Library for JavaScript (MSAL.js) выполните следующие действия:
- Убедитесь, что в системе установлен самый последний пакет SDK для разработчиков .NET или получите и установите его на .NET SDK: установщики и бинарные файлы. Для этого сценария настройка внутренних каналов NuGet не требуется.
- Настройте репозиторий
dotnet/aspnetcoreGitHub для разработки, следуя документации по сборке ASP.NET Core из исходного кода. Сделайте форк и клонируйте или скачайте ZIP-архив репозиторияdotnet/aspnetcoreGitHub. - Откройте файл
src/Components/WebAssembly/Authentication.Msal/src/Interop/package.jsonи задайте нужную версию@azure/msal-browser. Чтобы получить список выпущенных версий, посетите веб-сайт npm@azure/msal-browserи выберите вкладку Versions (Версии). - Создайте проект
Authentication.Msalв папкеsrc/Components/WebAssembly/Authentication.Msal/srcс помощью командыyarn buildв командной оболочке. - Если приложение использует сжатые ресурсы (Brotli/Gzip), необходимо сжать файл
Interop/dist/Release/AuthenticationService.js. - Скопируйте файл
AuthenticationService.jsи сжатые версии (.br/.gz) файла, если он создан, из папкиInterop/dist/Releaseв папкуpublish/wwwroot/_content/Microsoft.Authentication.WebAssembly.Msalв опубликованных ресурсах приложения.
Передача параметров настраиваемого поставщика
Определите класс для передачи данных в базовую библиотеку JavaScript.
Important
Структура класса должна соответствовать тому, что ожидает библиотека при сериализации JSON.System.Text.Json
В следующем примере демонстрируется класс ProviderOptions с атрибутами JsonPropertyName, которые соответствуют ожидаемым требованиям гипотетической библиотеки настраиваемого поставщика.
public class ProviderOptions
{
public string? Authority { get; set; }
public string? MetadataUrl { get; set; }
[JsonPropertyName("client_id")]
public string? ClientId { get; set; }
public IList<string> DefaultScopes { get; set; } = [ "openid", "profile" ];
[JsonPropertyName("redirect_uri")]
public string? RedirectUri { get; set; }
[JsonPropertyName("post_logout_redirect_uri")]
public string? PostLogoutRedirectUri { get; set; }
[JsonPropertyName("response_type")]
public string? ResponseType { get; set; }
[JsonPropertyName("response_mode")]
public string? ResponseMode { get; set; }
}
Зарегистрируйте параметры поставщика в системе внедрения зависимостей и задайте соответствующие значения:
builder.Services.AddRemoteAuthentication<RemoteAuthenticationState, RemoteUserAccount,
ProviderOptions>(options => {
options.ProviderOptions.Authority = "...";
options.ProviderOptions.MetadataUrl = "...";
options.ProviderOptions.ClientId = "...";
options.ProviderOptions.DefaultScopes = [ "openid", "profile", "myApi" ];
options.ProviderOptions.RedirectUri = "https://localhost:5001/authentication/login-callback";
options.ProviderOptions.PostLogoutRedirectUri = "https://localhost:5001/authentication/logout-callback";
options.ProviderOptions.ResponseType = "...";
options.ProviderOptions.ResponseMode = "...";
});
В предыдущем примере заданы URI перенаправления с использованием обычных строковых литералов. Доступны следующие варианты:
TryCreate с использованием IWebAssemblyHostEnvironment.BaseAddress:
Uri.TryCreate( $"{builder.HostEnvironment.BaseAddress}authentication/login-callback", UriKind.Absolute, out var redirectUri); options.RedirectUri = redirectUri;Конфигурация построителя хостов:
options.RedirectUri = builder.Configuration["RedirectUri"];wwwroot/appsettings.json:{ "RedirectUri": "https://localhost:5001/authentication/login-callback" }
Дополнительные ресурсы
ASP.NET Core