我有一个 Blazor Webassemble 应用程序,它使用 Azure B2C 对用户进行身份验证和授权。我能够成功登录并生成令牌,但是当我尝试调用我的 API 时,请求标头中没有令牌。
这是我的Program.cs
var builder = WebAssemblyHostBuilder.CreateDefault(args);
var baseAddress = builder.Configuration.GetValue<string>("BaseUrl");
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");
builder.Services.AddHttpClient("WebAPI", client => client.BaseAddress = new Uri(baseAddress))
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>()
.CreateClient("WebAPI"));
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAdB2C", options.ProviderOptions.Authentication);
options.ProviderOptions.DefaultAccessTokenScopes.Add("https://{domain}.onmicrosoft.com/{App ID}/Api.Access");
});
await builder.Build().RunAsync();
我是否缺少将令牌添加到 HttpClient 的一行?
我有一个 blazor 应用程序,它是
https://localhost:7280
并且它不包含 API。我还有另一个 api 项目,它有 API https://localhost:7018/WeatherForecast
。然后当我写这样的代码时:
builder.Services.AddHttpClient("WebAPI",client => client.BaseAddress = new Uri("https://localhost:7018"))
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
result = await Http.GetStringAsync("/WeatherForecast");
请求将在请求标头中没有不记名令牌的情况下发送,并会收到 401 错误。
我们还可以看到令牌已经生成了。
但是当我写这样的代码时:
builder.Services.AddHttpClient("WebAPI",client => client.BaseAddress = new Uri("https://localhost:7280"))
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
result = await Http.GetStringAsync("/WeatherForecast");
请求中会有一个访问令牌。
恐怕这就是这句话的意思......所以我们只能像我下面分享的那样手动生成令牌并将其添加到请求头中。
====================================================== =
我在我这边进行了测试,我也遇到了你的问题,这就是我用来手动生成令牌来调用 api 的方法。 使用我评论的代码。
@page "/profile"
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
@attribute [Authorize]
@inject IAccessTokenProvider TokenProvider
@inject HttpClient Http
<h3>User Profile</h3>
<button @onclick="call">
call api
</button>
<div>@result</div>
@code {
private string result = "no data now";
private async Task call()
{
try
{
result = await Http.GetStringAsync("https://localhost:7018/WeatherForecast");
}
catch (AccessTokenNotAvailableException exception)
{
exception.Redirect();
}
//var http = new HttpClient();
//http.BaseAddress = new Uri("https://localhost:7018/WeatherForecast");
//var tokenResult = await TokenProvider.RequestAccessToken(
// new AccessTokenRequestOptions
// {
// Scopes = new[] { "api://xxxx/Tiny.Read" }
// });
//if (tokenResult.TryGetToken(out var token))
//{
// http.DefaultRequestHeaders.Add("Authorization",
// $"Bearer {token.Value}");
// result = await http.GetStringAsync("https://localhost:7018/WeatherForecast");
//}
}
}
按照@Tiny Wang的回答我发现,如果你使用
BaseAddressAuthorizationMessageHandler
,它只会在authorization bearer
内通话时发送navigationManager.BaseUri
:
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Net.Http;
namespace Microsoft.AspNetCore.Components.WebAssembly.Authentication;
/// <summary>
/// A <see cref="DelegatingHandler"/> that attaches access tokens to outgoing <see cref="HttpResponseMessage"/> instances.
/// Access tokens will only be added when the request URI is within the application's base URI.
/// </summary>
public class BaseAddressAuthorizationMessageHandler : AuthorizationMessageHandler
{
/// <summary>
/// Initializes a new instance of <see cref="BaseAddressAuthorizationMessageHandler"/>.
/// </summary>
/// <param name="provider">The <see cref="IAccessTokenProvider"/> to use for requesting tokens.</param>
/// <param name="navigationManager">The <see cref="NavigationManager"/> used to compute the base address.</param>
public BaseAddressAuthorizationMessageHandler(IAccessTokenProvider provider, NavigationManager navigationManager)
: base(provider, navigationManager)
{
ConfigureHandler(new[] { navigationManager.BaseUri });
}
}
您必须创建一个像这样的自定义处理程序,并指定要包含的域
authorization bearer
:
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Net.Http;
namespace Microsoft.AspNetCore.Components.WebAssembly.Authentication;
/// <summary>
/// A <see cref="DelegatingHandler"/> that attaches access tokens to outgoing <see cref="HttpResponseMessage"/> instances.
/// Access tokens will only be added when the request URI is within the application's base URI.
/// </summary>
public class BaseAddressAuthorizationMessageHandler_Custom : AuthorizationMessageHandler
{
/// <summary>
/// Initializes a new instance of <see cref="BaseAddressAuthorizationMessageHandler"/>.
/// </summary>
/// <param name="provider">The <see cref="IAccessTokenProvider"/> to use for requesting tokens.</param>
/// <param name="navigationManager">The <see cref="NavigationManager"/> used to compute the base address.</param>
public BaseAddressAuthorizationMessageHandler_Custom(IAccessTokenProvider provider, NavigationManager navigationManager)
: base(provider, navigationManager)
{
ConfigureHandler(new[] { navigationManager.BaseUri, "https://localhost:7039" ❗❗❗ });
}
}
然后在你的程序中像这样使用:
builder.Services.AddScoped<BaseAddressAuthorizationMessageHandler_Custom>();
builder.Services.AddHttpClient<IEmployeeDataService, EmployeeDataService>(client => client.BaseAddress =
new Uri("https://localhost:7039")).AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler_Custom>();
builder.Services.AddHttpClient<ICountryDataService, CountryDataService>(client => client.BaseAddress =
new Uri("https://localhost:7039")).AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler_Custom>();
builder.Services.AddHttpClient<IJobCategoryDataService, JobCategoryDataService>(client => client.BaseAddress =
new Uri("https://localhost:7039")).AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler_Custom>();