Как использовать ASP. NET Web API 2.0 с токеном-носителем через настольное / консольное приложение - PullRequest
1 голос
/ 07 января 2020

У меня есть консольное / winform (не браузерное) приложение, которое использует ASP. NET Web API. Я реализовал аутентификацию на основе токенов, чтобы защитить свои веб-интерфейсы, и мне нужно выяснить, где лучше всего получить токен, если я следовал шаблону хранилища с использованием класса HttpClientHelper?

Чтобы получить токен:

private static string GetToken(string url, string userName, string password)
{
    var pairs = new List<KeyValuePair<string, string>>
    {
        new KeyValuePair<string, string>( "grant_type", "password" ), 
        new KeyValuePair<string, string>( "username", userName ), 
        new KeyValuePair<string, string> ( "Password", password )
    };

    var content = new FormUrlEncodedContent(pairs);

    ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;

    using(var client = new HttpClient())
    {
        var response = client.PostAsync(url + "Token", content).Result;
        return response.Content.ReadAsStringAsync().Result;
    }
}

Помощник HttpClient:

public sealed class HttpClientHelper<T, TResourceIdentifier> : IDisposable where T : class
{
        #region Constructors

        public HttpClientHelper(string serviceBaseAddress, string addressSuffix, string token)
        {
            _serviceBaseAddress = serviceBaseAddress;
            _addressSuffix = addressSuffix;
            _token = token
            _httpClient = MakeHttpClient();
        }

        #endregion

        #region CRUD

        public async Task<IEnumerable<T>> GetManyAsync()
        {
            var responseMessage = await _httpClient.GetAsync(_addressSuffix);

            if (responseMessage.IsSuccessStatusCode)
                return await responseMessage.Content.ReadAsAsync<IEnumerable<T>>();

            return null;
        }

        // Other CRUD operations

        #endregion

        #region Private Methods

        private HttpClient MakeHttpClient()
        {
            _httpClient = new HttpClient
            {
                BaseAddress = new Uri(_serviceBaseAddress),
                MaxResponseContentBufferSize = int.MaxValue,
                DefaultRequestHeaders = {Authorization = new AuthenticationHeaderValue("bearer", token)}
            };

            // Other settings

            return _httpClient;
        }

        #region IDisposable Members

        private void Dispose(bool disposing)
        {
            if (_disposed || !disposing) return;

            if (_httpClient != null)
            {
                var hc = _httpClient;
                _httpClient = null;
                hc.Dispose();
            }

            _disposed = true;
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        ~HttpClientHelper()
        {
            Dispose(false);
        }

        #endregion IDisposable Members
    }
}

Мой универсальный c класс:

internal class GenericRepository<T, TResourceIdentifier> : IDisposable, IGenericRepository<T, TResourceIdentifier>  where T : class
{
    private bool _disposed;
    protected HttpClientHelper<T, TResourceIdentifier> Client;

    protected GenericRepository(string serviceBaseAddress, string addressSuffix, string token)
    {
        Client = new HttpClientHelper<T, TResourceIdentifier>(serviceBaseAddress, addressSuffix, token);
    }

    public async Task<IEnumerable<T>> GetManyAsync()
    {
        return await Client.GetManyAsync();
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool disposing)
    {
        if (_disposed || !disposing) return;

        if (Client != null)
        {
            var mc = Client;
            Client = null;
            mc.Dispose();
        }

        _disposed = true;
    }
}

Пример пользовательского класса репозитория:

internal class CustomerRepository : GenericRepository<Customer, int> , ICustomerRepository
{
    internal CustomerRepository() : base(Properties.Settings.Default.Url, "Customers/", ***TOKEN***)
    {
    }
}

Использование:

private readonly ICustomerRepository _customerRepository = new CustomerRepository();

private async Task<Customer> GetCustomer(int customerId)   
{
    var customer = new Customer();

    try
    {
        customer = await _customerRepository.GetAsync(customerId);
    }
    catch (Exception ex)
    {
        // Log the error
    }

    return customer;
}

Где именно я могу получить / назначить токен без получения ошибки nullexception при объявлении моего IRepository?

1 Ответ

1 голос
/ 07 января 2020

Вы не должны создавать новый HttpClient каждый раз в любом случае. IDisposable может заставить вас думать, что вы должны, но это анти паттерн. Проверьте https://softwareengineering.stackexchange.com/questions/330364/should-we-create-a-new-single-instance-of-httpclient-for-all-requests для большого количества ресурсов по этому вопросу.

То же самое с токеном. Используйте свой токен, пока он активен. Проблемы безопасности для использования должны быть решены поставщиком удостоверений и временем ожидания. Вот почему существует токен refre sh для обработки срока действия токена.

https://oauth.net/2/grant-types/refresh-token/

...