如何解决重新设置单例httpclient的证书重新配置IHttpclientFactory?
使用C#、. NET Core 3.1
我在httpclient
中添加了一个单例startup.cs
:
services.AddHttpClient<IClientLogic,ClientLogicA>().ConfigurePrimaryHttpMessageHandler(() =>
{
var handler = new HttpClientHandler();
var cert= GetCertFromX();
handler.ClientCertificates.Add(cert);
return handler;
});
但是可以说,稍后在ClientLogicA
类中,我想更改证书,我该如何去做,并且该更改将继续存在,以供将来使用httpclient单例吗?
解决方法
因此,您要做的是修改HttpClient
产生的IHttpClientFactory
的证书。看起来Microsoft可能会在.NET 5中添加这种类型的功能,但与此同时,我们现在需要想出一种方法来实现它。
此解决方案将与命名HttpClient
和类型HttpClient
对象一起使用。
因此,这里的问题是要创建命名或键入的HttpClient
,可以随时更新绑定到HttpClient
的证书集合。问题是我们只能为HttpClient
设置一次创建参数。之后,IHttpClientFactory
一遍又一遍地重复使用这些设置。
因此,让我们开始看一下我们如何注入服务:
命名为HttpClient注入例程
services.AddTransient<IMyService,MyService>();
services.AddSingleton<ICertificateService,CertificateService>();
services.AddHttpClient("MyCertBasedClient").
ConfigurePrimaryHttpMessageHandler(sp =>
new CertBasedHttpClientHandler(
sp.GetRequiredService<ICertificateService>()));
键入HttpClient注入例程
services.AddSingleton<ICertificateService,CertificateService>();
services.AddHttpClient<IMyService,MyService>().
ConfigurePrimaryHttpMessageHandler(sp =>
new CertBasedHttpClientHandler(
sp.GetRequiredService<ICertificateService>()));
我们注入ICertificateService
作为 singleton ,该证书持有我们当前的证书,并允许其他服务对其进行更改。使用命名的IMyService
时手动注入HttpClient
,而使用类型为HttpClient
时,IMyService
将被自动注入。当IHttpClientFactory
创建我们的HttpClient
时,它将调用lambda并产生一个扩展的HttpClientHandler
,它将我们的ICertificateService
从我们的服务管道作为构造函数参数
下一部分是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
在另一个线程的请求中间,我们可能会陷入竞争状态。您可以通过使用SendAsync
或任何其他异步线程同步模式来保护SemaphoreSlim
中的代码来减轻这种情况,但这可能会引起瓶颈,因此我不必理会。如果您希望看到添加的内容,我将更新此答案。
我距离ASP.NET专家还很远,但是在阅读您的代码时看不到辛格尔顿。
我看到每次创建新客户端并添加证书。
如果要使用该代码,则需要更改GetCertFromX返回的内容。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。