gpt4 book ai didi

c# - 重新设置单例httpclient的证书(Reconfiguring IHttpclientFactory?)

转载 作者:行者123 更新时间:2023-12-05 03:50:38 28 4
gpt4 key购买 nike

使用 C#、.NET Core 3.1

我在 startup.cs 中添加了一个单例 httpclient:

services.AddHttpClient<IClientLogic, ClientLogicA>().ConfigurePrimaryHttpMessageHandler(() =>
{
var handler = new HttpClientHandler();

var cert= GetCertFromX();

handler.ClientCertificates.Add(cert);

return handler;
});

但是假设,稍后在 ClientLogicA 类中,我想更改证书,我该怎么做,更改是否会在将来使用 httpclient 单例时持续存在?

最佳答案

因此,您要做的是修改 IHttpClientFactory 生成的 HttpClient 的证书。看起来 Microsoft 可能会在 .NET 5 中添加这种类型的功能,但与此同时,我们现在需要想出一种方法来实现它。

此解决方案适用于命名 HttpClient 和类型化 HttpClient 对象。

所以这里的问题是创建命名或类型化的 HttpClient,其中绑定(bind)到 HttpClient 的证书集合可以随时更新。问题是我们只能为 HttpClient 设置一次创建参数。之后,IHttpClientFactory 一遍又一遍地重复使用这些设置。

那么,让我们先看看我们如何注入(inject)我们的服务:

Named HttpClient Injection Routine

services.AddTransient<IMyService, MyService>(); 

services.AddSingleton<ICertificateService, CertificateService>();

services.AddHttpClient("MyCertBasedClient").
ConfigurePrimaryHttpMessageHandler(sp =>
new CertBasedHttpClientHandler(
sp.GetRequiredService<ICertificateService>()));

Typed HttpClient Injection Routine

services.AddSingleton<ICertificateService, CertificateService>();

services.AddHttpClient<IMyService, MyService>().
ConfigurePrimaryHttpMessageHandler(sp =>
new CertBasedHttpClientHandler(
sp.GetRequiredService<ICertificateService>()));

我们将 ICertificateService 作为 singleton 注入(inject),它持有我们当前的证书并允许其他服务更改它。 IMyService 在使用 Named HttpClient 时手动注入(inject),而在使用 Typed HttpClient 时,IMyService 将自动注入(inject)。当 IHttpClientFactory 创建我们的 HttpClient 时,它将调用 lambda 并生成一个扩展的 HttpClientHandler,它接受我们的 ICertificateService 来 self 们的服务管道作为构造函数参数。

下一部分是 ICertificateService 的源代码。此服务使用“id”(它只是上次更新时间的时间戳)维护证书。

CertificateService.cs

public interface ICertificateService
{
void UpdateCurrentCertificate(X509Certificate cert);
X509Certificate GetCurrentCertificate(out long certId);
bool HasCertificateChanged(long certId);
}

public sealed class CertificateService : ICertificateService
{
private readonly object _certLock = new object();
private X509Certificate _currentCert;
private long _certId;
private readonly Stopwatch _stopwatch = new Stopwatch();

public CertificateService()
{
_stopwatch.Start();
}

public bool HasCertificateChanged(long certId)
{
lock(_certLock)
{
return certId != _certId;
}
}

public X509Certificate GetCurrentCertificate(out long certId)
{
lock(_certLock)
{
certId = _certId;
return _currentCert;
}
}

public void UpdateCurrentCertificate(X509Certificate cert)
{
lock(_certLock)
{
_currentCert = cert;
_certId = _stopwatch.ElapsedTicks;
}
}
}

这最后一部分是实现自定义 HttpClientHandler 的类。有了它,我们可以连接到客户端发出的所有 HTTP 请求。如果证书已更改,我们会在发出请求之前将其换掉。

CertBasedHttpClientHandler.cs

public sealed class CertBasedHttpClientHandler : HttpClientHandler
{
private readonly ICertificateService _certService;
private long _currentCertId;

public CertBasedHttpClientHandler(ICertificateService certificateService)
{
_certService = certificateService;
var cert = _certService.GetCurrentCertificate(out _currentCertId);
if(cert != null)
{
ClientCertificates.Add(cert);
}
}

protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
if(_certService.HasCertificateChanged(_currentCertId))
{
ClientCertificates.Clear();
var cert = _certService.GetCurrentCertificate(out _currentCertId);
if(cert != null)
{
ClientCertificates.Add(cert);
}
}
return base.SendAsync(request, cancellationToken);
}
}

现在我认为最大的缺点是,如果 HttpClient 正处于另一个线程的请求中间,我们可能会遇到竞争条件。您可以通过使用 SemaphoreSlim 或任何其他异步线程同步模式保护 SendAsync 中的代码来缓解这种情况,但这可能会导致瓶颈,所以我没有打扰这样做。如果您想看到添加的内容,我会更新此答案。

关于c# - 重新设置单例httpclient的证书(Reconfiguring IHttpclientFactory?),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63375951/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com