简体   繁体   English

如何在 Blazor 服务器应用程序的服务注册中向 HttpClient 注入访问令牌?

[英]How to inject access token to HttpClient in Service registration in Blazor server app?

I have Blazor server app to use Identity Server 4 for authentication and authorization purposes.我有 Blazor 服务器应用程序来使用 Identity Server 4 进行身份验证和授权。 And, I have a protected api (JWT token) to provide data to Blazor server app.而且,我有一个受保护的 api(JWT 令牌)来向 Blazor 服务器应用程序提供数据。

I have followed this post to get access token and pass it to HttpClient during service registration as below,我已经按照这篇文章获取访问令牌并在服务注册期间将其传递给HttpClient ,如下所示,

Startup.cs启动.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应用程序初始化器.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 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);
    }
}

I am unable to assign access token to HttpClient object since it was null always.我无法将访问令牌分配给HttpClient object,因为它始终是 null。 Did I miss anything?我错过了什么吗?

I am unable to assign access token to HttpClient object since it was null always.我无法将访问令牌分配给 HttpClient object,因为它始终是 null。

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.由于令牌是在请求期间从当前HttpContext访问的,因此在注册客户端时将不可用。

This makes trying to add a default header not ideal.这使得尝试添加默认 header 并不理想。

Consider changing the approach.考虑改变方法。

Create a message handler to intercept/extract and inject the desired header during the scope of a request创建消息处理程序以在请求的 scope 期间拦截/提取并注入所需的 header

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);
    }
}

include the message handler in the pipeline when registering the client, like in this simplified example for adding the API named client在注册客户端时在管道中包含消息处理程序,就像在这个简化示例中添加名为客户端的 API

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
    
    //...
}

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. ServiceCollectionRegistration.InstallServices过于复杂,并且难以维护 (IMO),因为它违反了关注点分离 (SoC) 违规和不正确地调用BuildServiceProvider ,这将在以后导致问题。

Use the provider in the factory delegate when registering your types so the proper IServiceProvider is used to resolve service dependencies.注册类型时使用工厂委托中的提供程序,以便使用正确的IServiceProvider来解决服务依赖关系。

//...

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);
        });
    });
}

//...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在 Blazor 服务器应用程序中访问 WCF 服务 - How to Access WCF service in Blazor Server App 如何使用 OIDC 在 Blazor 服务器应用程序中获取访问令牌声明? - How to get access token claims in a Blazor Server app using OIDC? 如何将服务注入 Blazor? - How to inject a service into Blazor? 在 Blazor 服务器应用程序中,是否可以将 GraphServiceClient 注入范围服务? - In a Blazor Server app, is it possible to inject a GraphServiceClient into a scoped service? 如何从 blazor(服务器端)Web 应用程序获取访问令牌? - How do I get the access token from a blazor (server-side) web app? 如何在 Blazor 客户端应用程序的服务中调用 HttpClient - How Do I Call HttpClient in Service for a Blazor Client-Side App 如何将服务注入 Blazor Server 中的实体框架 AddDbContextFactory lambda 函数? - How can I inject a service into an Entity Framework AddDbContextFactory lambda function in Blazor Server? 如何在 Blazor 服务器应用程序中注入 ClaimsPrincipal - How to inject a ClaimsPrincipal in a Blazor Server application 如何从 Blazor 服务器应用程序(.NET 5)中的服务 class 检索数据? - How to retrieve data from a service class in Blazor Server App(.NET 5)? 添加令牌的 blazor 服务器应用程序身份验证问题 - blazor server app Auth problem with adding token
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM