在 Blazor wasm 中自动将访问令牌附加到 HTTP 客户端

Automatically Attaching Access Token to HTTP Client in Blazor wasm

我正在为我的 Blazor wasm 应用程序使用 open id connect 身份提供程序,并希望将访问令牌附加到 http 客户端,如 this 文章中所述。

但是,每当我创建 http 客户端并尝试使用它时,我都会收到 AccessTokenNotAvailableException,即使是在登录时也是如此。

这是我的:

在Program.cs

// Add service for CustomAuthorizationMessageHandler
builder.Services.AddScoped<CustomAuthorizationMessageHandler>();

// Http client for requests to API
builder.Services.AddHttpClient("API", client =>
{
    // Set base address to API url
    client.BaseAddress = new Uri("https://localhost:44370");

    // Set request headers to application/json
    client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
}).AddHttpMessageHandler<CustomAuthorizationMessageHandler>();

// Auth0 authentication setup
builder.Services.AddOidcAuthentication(options =>
{
    builder.Configuration.Bind("Auth0", options.ProviderOptions);
    options.ProviderOptions.ResponseType = "code";
});

CustomAuthorizationHandler.cs

public class CustomAuthorizationMessageHandler : AuthorizationMessageHandler
{
    public CustomAuthorizationMessageHandler(IAccessTokenProvider provider,
        NavigationManager navigationManager)
        : base(provider, navigationManager)
    {
        ConfigureHandler(
            authorizedUrls: new[] { "https://localhost:44370" },
            scopes: new[] { "admin" });
    }
}

尝试访问 api

try
{
    var client = ClientFactory.CreateClient("API");
    message = await client.GetStringAsync("api/message");
}
catch (AccessTokenNotAvailableException e)
{
    message = "You CANNOT access the api. Please log in";
}

目前,当删除 Program.cs 中的 AddHttpMessageHandler 调用时,下面的代码可以从 api 获取消息,但我不想每次都获取访问令牌api 电话。 TokenProvider 是 IAccessTokenProvider 类型,被注入.

var client = ClientFactory.CreateClient("API");
using (var requestMessage = new HttpRequestMessage(HttpMethod.Get, "api/message"))
{
    var tokenResult = await TokenProvider.RequestAccessToken();

    if (tokenResult.TryGetToken(out var token))
    {
        requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token.Value);
        var response = await client.SendAsync(requestMessage);
        if(response.IsSuccessStatusCode)
        {
            message = await response.Content.ReadFromJsonAsync<string>();
        }
        else
        {
            message = await response.Content.ReadAsStringAsync();
        }
    }
    else
    {
        message = "You CANNOT access the api. Please log in";
    }
}

我该如何解决这个问题,以免每次都收到 AccessTokenNotAvailableException?

已经明确指出有two ways为传出请求配置消息处理程序,建议实现自定义消息处理程序。 再次…………您实现自定义消息处理程序以配置消息处理程序。这是定制的唯一目的。当然,您可以重写 SendAsync 方法,如果这样的操作到位,您知道自己在做什么,并且您会适当地这样做。但不是引入错误并实际上使 SendAsync 无效的方法。

这是自定义消息处理程序的实现:

using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

--

public class CustomAuthorizationMessageHandler : AuthorizationMessageHandler
    {
        public CustomAuthorizationMessageHandler(IAccessTokenProvider provider,
            NavigationManager navigationManager)
            : base(provider, navigationManager)
        {
            ConfigureHandler(
               authorizedUrls: new[] { "https://localhost:44370" });
    
        }
    }   

上面解决了你遇到的问题。。。也就是说,配置作用域属性(scopes: new[] { "admin" });),可以省略。

以下是一些改进代码的建议: 如下创建命名的 HttpClient 服务。

builder.Services.AddTransient(sp => sp.GetRequiredService<IHttpClientFactory> 
   ().CreateClient("ServerAPI"));

然后像这样将其注入到您的组件中:@inject HttpClient Http

并像这样使用它:

protected override async Task OnInitializedAsync()
    {
        try
        {
           message= await Http.GetFromJsonAsync<string>("api/message");
        }
        catch (AccessTokenNotAvailableException exception)
        {
            exception.Redirect();
        }
    } 

注意:为了使用 GetFromJsonAsync 方法,您应该安装 System.Net.Http.Json 包裹

请注意,以上是处理Http请求的正确方式。这包括将用户重定向到登录页面。当然,您可以生成任何要显示给用户的消息,但重定向是正确的方法。

应用以上建议的更改后,您的 Program.Main 方法应具有以下设置(注意顺序):

builder.Services.AddScoped<CustomAuthorizationMessageHandler>();

            builder.Services.AddHttpClient("ServerAPI", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
               .AddHttpMessageHandler<CustomAuthorizationMessageHandler>();
                
            builder.Services.AddTransient(sp => 
   sp.GetRequiredService<IHttpClientFactory>().CreateClient("ServerAPI"));