如何在Blazor服务器应用程序的服务注册中向HttpClient注入访问令牌? [英] How to inject access token to HttpClient in Service registration in Blazor server app?

查看:115
本文介绍了如何在Blazor服务器应用程序的服务注册中向HttpClient注入访问令牌?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有Blazor服务器应用程序,可使用Identity Server 4进行身份验证和授权.而且,我有一个受保护的api(JWT令牌)可以将数据提供给Blazor服务器应用程序.

I have Blazor server app to use Identity Server 4 for authentication and authorization purposes. And, I have a protected api (JWT token) to provide data to Blazor server app.

我已关注这篇文章以获取访问令牌并将其在服务期间传递给 HttpClient 注册如下,

I have followed this post to get access token and pass it to HttpClient during service registration as below,

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddAntDesign();

    services.AddRazorPages();
    services.AddServerSideBlazor();
    services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    services.AddScoped<TokenProvider>(); <--
    ApplicationInitializer.Initialize(Configuration, services);
}

ApplicationInitializer.cs

public static class ApplicationInitializer
{
    public static void Initialize(IConfiguration configuration, IServiceCollection services)
    {
        var installers = typeof(Startup).Assembly.ExportedTypes
            .Where(w => typeof(IInstaller).IsAssignableFrom(w) && !w.IsInterface && !w.IsAbstract)
            .Select(Activator.CreateInstance)
            .Cast<IInstaller>()
            .ToList();

        installers.ForEach(installer => installer.InstallServices(services, configuration));
    }
}

ServiceCollectionRegistration.cs

public class ServiceCollectionRegistration : IInstaller
{
    public void InstallServices(IServiceCollection services, IConfiguration configuration)
    {
        //Register api (NSwag generated) clients with HttpClient from HttpClientFactory
        var apiOptions = new ApiOptions();
        configuration.GetSection(nameof(ApiOptions)).Bind(apiOptions);
        services.AddHttpClient("api", (provider, client) =>
        {
            client.BaseAddress = new Uri(apiOptions.ApiUrl);

            //This is not working as expected. Access Token is always null
            var tokenProvider = services.BuildServiceProvider().GetRequiredService<TokenProvider>();
            var accessToken = tokenProvider.AccessToken;
            if (accessToken != null)
                client.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken);
        });

        var asm = Assembly.GetExecutingAssembly();
        var interfaces = asm.GetInterfaces();
        foreach (var interfaceType in interfaces.Where(x => x.Name.EndsWith("Client")))
        {
            var currentInterfaceType = interfaceType;
            var implementations = asm.GetImplementationsOfInterface(interfaceType);
            implementations.ToList().ForEach(i =>
            {
                services.AddScoped(currentInterfaceType, ctx =>
                {
                    var clientFactory = services.BuildServiceProvider().GetRequiredService<IHttpClientFactory>();
                    var httpClient = clientFactory.CreateClient("api");
                    return Activator.CreateInstance(i, httpClient);
                });
            });
        }

        //Register all provider type to their interface type using reflection
        foreach (var interfaceType in interfaces.Where(x => !x.Name.EndsWith("Client")))
        {
            var currentInterfaceType = interfaceType;
            var implementations = asm.GetImplementationsOfInterface(interfaceType);
            if (implementations.Count > 1)
                implementations.ToList().ForEach(i => services.AddScoped(currentInterfaceType, i));
            else
                implementations.ToList().ForEach(i => services.AddScoped(currentInterfaceType, i));
        }
        new AutoMapperConfiguration().RegisterProfiles(services);
    }
}

我无法将访问令牌分配给 HttpClient 对象,因为它始终为null.我有什么想念吗?

I am unable to assign access token to HttpClient object since it was null always. Did I miss anything?

推荐答案

我无法将访问令牌分配给HttpClient对象,因为它始终为null.

I am unable to assign access token to HttpClient object since it was null always.

由于令牌是在请求期间从当前 HttpContext 访问的,因此在注册客户端时将不可用.

Since the token is to be accessed from the current HttpContext during a request, it wont be available at the time when registering the client.

这使尝试添加默认标头不理想.

This makes trying to add a default header not ideal.

考虑更改方法.

创建一个消息处理程序,以在请求范围内拦截/提取并注入所需的标头

Create a message handler to intercept/extract and inject the desired header during the scope of a request

public class TokenHandler : DelegatingHandler {
    private readonly IHttpContextAccessor accessor;
    
    public TokenHandler(IHttpContextAccessor accessor) => this.accessor = accessor;

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
        //get the token
        var accessToken = await accessor.HttpContext.GetTokenAsync("access_token");
        //add header
        request.Headers.Authorization =
            new AuthenticationHeaderValue("Bearer", accessToken);
        //continue down stream request
        return await base.SendAsync(request, cancellationToken);
    }
}

在注册客户端时将消息处理程序包括在管道中,例如在此简化示例中,添加了名为客户端的API

include the message handler in the pipeline when registering the client, like in this simplified example for adding the API named client

public void ConfigureServices(IServiceCollection services) {
    services.AddAntDesign();

    services.AddRazorPages();
    services.AddServerSideBlazor();
    services.AddHttpContextAccessor();
    services.AddScoped<TokenHandler>();
    
    //Register api (NSwag generated) clients with HttpClient from HttpClientFactory
    ApiOptions apiOptions = Configuration.GetSection(nameof(ApiOptions)).Get<ApiOptions>();
    services
        .AddHttpClient("api", (client) => {
            client.BaseAddress = new Uri(apiOptions.ApiUrl);
        })
        .AddHttpMessageHandler<TokenHandler>(); //inject token using our token handler
    
    //...
}

ServiceCollectionRegistration.InstallServices 过于复杂,使用它很难维护(IMO)的关注点分离(SoC)违规行为和不正确地调用会导致问题的 BuildServiceProvider 以后.

The ServiceCollectionRegistration.InstallServices is overly complicated and will be difficult to maintain (IMO) with it Separation of Concerns (SoC) violations and improperly calling BuildServiceProvider that will cause problems later on.

注册类型时,请在工厂委托中使用提供程序,以便使用正确的 IServiceProvider 来解析服务依赖项.

Use the provider in the factory delegate when registering your types so the proper IServiceProvider is used to resolve service dependencies.

//...

foreach (var interfaceType in interfaces.Where(x => x.Name.EndsWith("Client"))) {
    var currentInterfaceType = interfaceType;
    var implementations = asm.GetImplementationsOfInterface(interfaceType);
    implementations.ToList().ForEach(i => {
        services.AddScoped(currentInterfaceType, provider => {
            var clientFactory = provider.GetRequiredService<IHttpClientFactory>();
            var httpClient = clientFactory.CreateClient("api");
            return Activator.CreateInstance(i, httpClient);
        });
    });
}

//...

这篇关于如何在Blazor服务器应用程序的服务注册中向HttpClient注入访问令牌?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆