我们有一个单件服务结构服务,需要与分区服务进行通信,这两个服务都在基于证书的身份验证的安全集群中运行。我们正在使用ServicePartitionClient
进行讨论。以下是ICommunicationClient
所要求的ICommunicationClientFactory
和ServicePartitionClient
实施的简化版本。
客户:
public class HttpCommunicationClient : ICommunicationClient
{
// Lots of fields omitted for simplicity.
public HttpCommunicationClient(HttpClientWrapper client, Uri baseAddress)
{
this.HttpClient = client;
this.BaseAddress = baseAddress;
}
public Uri BaseAddress { get; set; }
// Wraps System.Net.Http.HttpClient to do stuff like add default headers
public HttpClientWrapper HttpClient { get; }
public ResolvedServicePartition ResolvedServicePartition { get; set; }
public string ListenerName { get; set; }
public ResolvedServiceEndpoint Endpoint { get; set; }
public Uri GetUri(string relativeUri)
{
return new Uri(this.BaseAddress, relativeUri);
}
}
工厂:
// Note that this base class is under the
// Microsoft.ServiceFabric.Services.Communication.Client namespace,
// it's not something we wrote
public class HttpCommunicationClientFactory :
CommunicationClientFactoryBase<HttpCommunicationClient>
{
// Lots of fields omitted for simplicity.
private readonly HttpClientWrapper client;
public HttpCommunicationClientFactory(
ServicePartitionResolver resolver,
IEnumerable<IExceptionHandler> exceptionHandlers)
: base(resolver, exceptionHandlers, null)
{
// There's a bunch of other args that are omitted for clarity.
this.client = new HttpClientWrapper();
}
protected override Task<HttpCommunicationClient> CreateClientAsync(
string endpoint,
CancellationToken cancellationToken)
{
HttpCommunicationClient client = new HttpCommunicationClient(
this.client,
new Uri(endpoint));
if (this.ValidateClient(endpoint, client))
{
return Task.FromResult(client);
}
else
{
throw new ArgumentException();
}
}
protected override bool ValidateClient(HttpCommunicationClient client)
{
// Not much to validate on httpclient
return true;
}
protected override bool ValidateClient(
string endpoint,
HttpCommunicationClient client)
{
return !string.IsNullOrWhiteSpace(endpoint);
}
protected override void AbortClient(HttpCommunicationClient client)
{
}
}
这是一个如何构建和使用所有内容的例子:
internal class FooFabricClient : IDisposable
{
// Lots of fields omitted for simplicity.
private readonly HttpCommunicationClientFactory clientFactory;
private readonly ServicePartitionClient<HttpCommunicationClient> partitionClient;
public FooFabricClient(Uri fabricUri, ServicePartitionKey partitionKey = null)
{
this.clientFactory = new HttpCommunicationClientFactory(
ServicePartitionResolver.GetDefault());
this.partitionClient = new ServicePartitionClient<HttpCommunicationClient>(
this.clientFactory,
fabricUri,
partitionKey,
retrySettings: new OperationRetrySettings());
}
public async Task<Foo> GetFooAsync()
{
return await this.partitionClient.InvokeWithRetryAsync(async (client) =>
{
// Note: See above for actual GetUri() implementation and context,
// but this will give us a Uri composed of the client's BaseAddress
// (passed in during HttpCommunicationClientFactory.CreateClientAsync())
// followed by "/foo"
Uri requestUri = client.GetUri("foo");
return await client.HttpClient.GetAsync<Foo>(requestUri);
});
}
现在,问题在于,当我致电GetFooAsync()
时,会抛出一个异常说:
无法为SSL / TLS安全通道建立信任关系。 ---&GT; System.Security.Authentication.AuthenticationException:根据验证过程,远程证书无效。
经过一些调试后,我们发现这很可能是因为我们将内部服务结构IP地址(例如10.0.0.4
)作为HttpCommunicationClient.BaseAddress
获取,因此当我们进行API调用时服务器证书不会对请求中的域进行验证。作为临时修复,我们在呼叫服务中完成了以下操作:
ServicePointManager.ServerCertificateValidationCallback += (a, b, c, d) => true;
当然,在验证服务器证书时,我们不是只是盲目地说“是的,看起来不错”,那么我们应该如何解决这个问题呢?是否有另一种方法来设置客户端,以便他们可以在请求上正确验证服务器证书而无需我们自己的回调?或者我们只需要在配置中添加可接受的指纹,并与ServicePointManager
回调中的那些或其他内容进行比较?