【问题标题】:How to consume ASP.NET Web API 2.0 with bearer token via a desktop/console application如何通过桌面/控制台应用程序使用带有不记名令牌的 ASP.NET Web API 2.0
【发布时间】:2020-01-07 17:43:27
【问题描述】:

我有一个使用 ASP.NET Web API 的控制台/winform(非浏览器)应用程序。我已经实现了基于令牌的身份验证来保护我的 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
    }
}

我的通用类:

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;
}

在我的 IRepository 声明中,我究竟在哪里获取/分配令牌而不会出现 nullexception 错误?

【问题讨论】:

    标签: c# .net winforms authentication asp.net-web-api


    【解决方案1】:

    在任何情况下,您都不应该每次都实例化一个新的HttpClient。 IDisposable 可能会让你认为你应该这样做,但它是一种反模式。查看https://softwareengineering.stackexchange.com/questions/330364/should-we-create-a-new-single-instance-of-httpclient-for-all-requests 获取有关此事的大量资源。

    与令牌相同。只要您的令牌处于活动状态,就可以使用它。使用的安全问题应该由身份提供者和超时来解决。这就是为什么有一个刷新令牌来处理令牌过期。

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

    【讨论】:

    • 关于 HttpClient 的使用很好,我不是。至于在正确的位置获取和分配令牌,我仍然不确定在哪里执行此操作。我已经更新了我的帖子以反映我此后添加的内容。关于在哪里(显然在表单构造函数中不是正确的位置,因为我在初始化存储库时得到 NullReferenceException)的任何建议,我得到令牌并分配它?
    猜你喜欢
    • 1970-01-01
    • 2014-08-06
    • 1970-01-01
    • 1970-01-01
    • 2021-02-23
    • 2018-01-29
    • 2020-12-21
    • 2016-12-05
    • 2018-06-29
    相关资源
    最近更新 更多