如何在.Net Core中使用单个HttpClient实例发送不同的客户端证书到不同请求?

5
推荐使用单个实例重复使用HttpClient。但从API来看,添加证书的方式是在实例上进行而不是每个请求。如果我们添加了两个证书,如何确保“cert 1”仅发送到“one.somedomain.com”?
//A handler is how you add client certs (is there any other way?)
var _clientHandler = new HttpClientHandler();

//Add multiple certs
_clientHandler.ClientCertificates.Add(cert1);
_clientHandler.ClientCertificates.Add(cert2);
_clientHandler.ClientCertificateOptions = ClientCertificateOption.Manual;


//Pretend this is our long-living HttpClient
var client = new HttpClient(_clientHandler);

//Now if we make a post request, will both certs be used?
using (HttpResponseMessage response = _client.PostAsync("https://one.somedomain.com", content).Result)
{
    //...elided...
 }

2
看起来您需要实现自己的 HttpClientHandler 并指定所需的行为。 - Krivitskiy Grigoriy
1
我认为为每个服务器创建新的HttpClient会救命。您可以将它们放在一个字典中,其中键是服务器基地址。 - ilkerkaran
@KrivitskiyGrigoriy 这可能是唯一的选择,但我想知道是否会在每个请求上增加太多额外的处理,或者是否有一种方法可以保持它的速度?此外,您是否知道如何做到这一点? - Don Rhummy
2个回答

2
抱歉,年底工作繁忙。您可以尝试实现以下内容:
public class CustomHttpHandler : HttpClientHandler
{
    private readonly Dictionary<string, X509Certificate> _certMap;

    public CustomHttpHandler():base()
    {
        _certMap = new Dictionary<string, X509Certificate>() { { "server1name", new X509Certificate("cert1") }, { "server2name", new X509Certificate("cert2") } };
    }

    protected override async Task<HttpResponseMessage> SendAsync(
            HttpRequestMessage request,
            CancellationToken cancellationToken)
    {
        string serverName = request.RequestUri.Host;
        if (ClientCertificates.Contains(_certMap[serverName]))
        {
            try
            {
                var response = await base.SendAsync(request, cancellationToken);
                return response;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.ReadKey();
                throw;
            }
        }
        else
        {
            ClientCertificates.Clear();
            ClientCertificates.Add(_certMap[serverName]);

            try
            {
                var response = await base.SendAsync(request, cancellationToken);
                return response;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.ReadKey();
                throw;
            }
        }
    }
}

这只是一个想法,未经测试。 或者,您可以在RequestMessage实例中使用Headers集合。 本文介绍了这个主题:https://damienbod.com/2019/09/07/using-certificate-authentication-with-ihttpclientfactory-and-httpclient/


有没有办法在退出之前处理字典的值。 - Tanul
我认为这个解决方案不是线程安全的。 - dube

-3

这是一个建议,不过你可以使用 "using" 语句。

由于 HttpClient 是 IDisposable 接口,所以你应该使用类似以下的方式:

using(var client = new HttpClient(_clientHandler))
{
    //Your code here
}

2
这是微软明确不推荐的做法,会引入性能问题、内存泄漏和连接句柄泄漏。 - Jeff

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接