如何在Blazor服务器应用程序的服务注册中向HttpClient注入访问令牌? [英] How to inject access token to HttpClient in Service registration in Blazor server app?
问题描述
我有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屋!