Net Core Windows Service с IHttpClientFactory с использованием учетных данных учетной записи службы - PullRequest
0 голосов
/ 17 сентября 2018

Цель (чтобы избежать проблемы X Y): создать службу Windows, которая регулярно запрашивает API REST. Для API REST требуется проверка подлинности Windows (интегрированная).

У меня есть рабочий каркас для службы Windows, которая использует IHostedService для запуска процесса, работающего долго. Этот IHostedService использует IRestClient для запроса REST API. Чтобы избежать необходимости вручную управлять временем жизни HttpClients, я пытаюсь ввести типизированный HttpClient, используя AddHttpClient. Это работает, пока я не попытаюсь настроить HttpMessageHandler. Везде, где я могу найти, где я мог бы установить HttpClient UseDefaultCredentials = true, кажется, не устанавливает их.

Я могу вручную настроить эту опцию в службе RestClient. Это передаст заголовок, но если я попытаюсь использовать CredentialCache.DefaultCredentials или DefaultNetworkCredentails, они вернут пустые значения.

Это также может быть недоразумением с моей стороны относительно того, как взаимодействуют все эти абстракции.

Program.cs

private static async Task Main(string[] args)
{
    var builder = new HostBuilder()
        .ConfigureAppConfiguration((hostingContext, config) =>
        {
            // Settings
        })
        .ConfigureServices((hostContext, services) =>
        {
            // Other services omitted

            services.AddSingleton<ITimer, CustomTimersTimer>();

            services.AddHttpClient<IRestClient, RestClient>()
                .ConfigureHttpMessageHandlerBuilder(config => new HttpClientHandler()
                {
                    // Seems to have no effect
                    UseDefaultCredentials = true,
                    UseProxy = false,
                    UseCookies = false
                });
            services.AddHostedService<RestDataGatheringService>();
        })
        .ConfigureLogging((hostingContext, logging) =>
        {
            // Logging
        }

RestDataGatheringService.cs

public class RestDataGatheringService : IHostedService, IDisposable
{
    private ITimer _timer;
    private readonly ILogger<RestDataGatheringService> _logger;
    private readonly IRestClient _client;

    public RestDataGatheringService(IRestClient restClient, ITimer timer, ILogger<RestDataGatheringService> logger)
    {
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        _timer = timer ?? throw new ArgumentNullException(nameof(timer));
        _client = restClient ?? throw new ArgumentNullException(nameof(restClient));
    }
}

RestClient.cs

public class RestClient : IRestClient
{
    private readonly HttpClient _client;
    private readonly ILogger<RestClient> _logger;
    private readonly IOptions<ConnectionSettings> _config;


    public RestClient(HttpClient httpClient, IOptions<ConnectionSettings> config, ILogger<RestClient> logger)
    {
        _client = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
        _config = config ?? throw new ArgumentNullException(nameof(config));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));

        _client.BaseAddress = new Uri(_config.Value.VCenterConnectionString);
    }

    public async Task Connect()
    {
        string login = "/rest/login";

        // Uncommenting this section adds the request header, although DefaultNetworkCredentials has an empty username and password.
        // Without it, the Authorization header is not present as I would expect it to be
        // _client.DefaultRequestHeaders.Authorization =
        //    new System.Net.Http.Headers.AuthenticationHeaderValue(
        //        "Basic",
        //        Convert.ToBase64String(
        //            System.Text.ASCIIEncoding.ASCII.GetBytes(
        //                string.Format("{0}:{1}",
        //                    System.Net.CredentialCache.DefaultNetworkCredentials.UserName,
        //                    System.Net.CredentialCache.DefaultNetworkCredentials.Password))));

        var result = await _client.PostAsync(login, null);

    }

Я видел некоторую документацию , в которой говорится, что текущее имя пользователя и пароль не могут быть извлечены из DefaultCredentials во время отладки, но такое поведение сохраняется, даже если я dotnet publish -c Release -r win7-x64 и запускаю исполняемый файл. Я также предполагаю, что то же самое верно для DefaultNetworkCredentials.

После прочтения этого вопроса Я посмотрел на внутренний-внутренний-внутренний обработчик RestDataGatheringService._client, и, похоже, к нему не применена конфигурация, указанная в Program.cs.

Решения здесь , здесь и здесь у меня не сработали.

...