我有 Blazor 服务器应用程序来使用 Identity Server 4 进行身份验证和授权。而且,我有一个 protected api(JWT token )来向 Blazor 服务器应用程序提供数据。
我已经按照this post获取访问 token 并在服务注册期间将其传递给HttpClient
,如下所示,
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);
}
}
我无法将访问 token 分配给 HttpClient
对象,因为它始终为 null。我错过了什么吗?
最佳答案
I am unable to assign access token to HttpClient object since it was null always.
由于在请求期间要从当前 HttpContext
访问 token ,因此在注册客户端时该 token 将不可用。
这使得尝试添加默认 header 并不理想。
考虑改变方法。
创建消息处理程序以在请求范围内拦截/提取并注入(inject)所需的 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);
}
}
在注册客户端时将消息处理程序包含在管道中,就像在这个添加名为 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
//...
}
ServiceCollectionRegistration.InstallServices
过于复杂,并且难以维护 (IMO)。 关注点分离 (SoC) 违规以及不正确地调用 BuildServiceProvider
会导致问题稍后。
在注册类型时使用工厂委托(delegate)中的提供程序,以便使用正确的 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);
});
});
}
//...
关于c# - 如何在 Blazor 服务器应用程序的服务注册中将访问 token 注入(inject)到 HttpClient?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66325590/