Refresh Token using Static HttpClient

前端 未结 1 1416
执笔经年
执笔经年 2021-01-13 16:10

Using VS 2017 .Net 4.5.2

I have the following class

public static class MyHttpClient
{
    //fields
    private static Lazy         


        
相关标签:
1条回答
  • 2021-01-13 16:46

    update: added the SemaphoreSlim to "lock" the refresh transaction

    disclaimer: not tested, might need some tweaking

    note 1: the semaphore needs to be in a try/catch/finaly block to ensure release if an error is thrown.

    note 2: this version will queue the refresh token calls which significant degrades performance if load is high. To fix this; use a bool indicator to check if the refresh is occurred. This might be a static bool for example

    The goal is to only use the refresh token if needed. A fixed interval won't help you because, one day, this interval might change. The correct way to handle this is to retry if a 403 occurs.

    You can use a HttpClientHandler to work with your HttpClient.

    Override the SendAsync, to handle and retry the 403.

    For this to work you'll need this constructor of httpclient:

    From the top of my (semi) head, it must be something like this:

    the HttpMessageHandler

    public class MyHttpMessageHandler : HttpMessageHandler
    {
        private static SemaphoreSlim sem = new SemaphoreSlim(1);
    
        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {  
        var response = await base.SendAsync(request, cancellationToken);
    
        //test for 403 and actual bearer token in initial request
        if (response.StatusCode == HttpStatusCode.Unauthorized &&
            request.Headers.Where(c => c.Key == "Authorization")
                    .Select(c => c.Value)
                    .Any(c => c.Any(p => p.StartsWith("Bearer"))))
            {
    
                //going to request refresh token: enter or start wait
                await sem.WaitAsync();
    
                //some typical stuff
                var pairs = new List<KeyValuePair<string, string>>
                {
                    new KeyValuePair<string, string>("grant_type", "refresh_token"),
                    new KeyValuePair<string, string>("refresh_token", yourRefreshToken),
                    new KeyValuePair<string, string>("client_id", yourApplicationId),
                };
    
                //retry do to token request
                using ( var refreshResponse = await base.SendAsync(
                    new HttpRequestMessage(HttpMethod.Post, 
                       new Uri(new Uri(Host), "Token")) 
                       { 
                          Content = new FormUrlEncodedContent(pairs) 
                       }, cancellationToken))
                {
                    var rawResponse = await refreshResponse.Content.ReadAsStringAsync();
                    var x = JsonConvert.DeserializeObject<RefreshToken>(rawResponse);
    
                    //new tokens here!
                    //x.access_token;
                    //x.refresh_token;
    
                    //to be sure
                    request.Headers.Remove("Authorization");
                    request.Headers.Add("Authorization", "Bearer " + x.access_token);
    
                    //headers are set, so release:
                    sem.Release();  
    
                    //retry actual request with new tokens
                    response = await base.SendAsync(request, cancellationToken);
    
                }
            }
    
            return response;
        }
    }
    }
    

    send example, with SendAsync (could also be GetAsync) etc.

    public async Task<int> RegisterAsync(Model model)
    {
        var response = await YourHttpClient
           .SendAsync(new HttpRequestMessage(HttpMethod.Post, new Uri(BaseUri, "api/Foo/Faa"))
        {  
            Content = new StringContent(
               JsonConvert.SerializeObject(model),
               Encoding.UTF8, "application/json")
        });
    
        var result = await response.Content.ReadAsStringAsync();
        return 0;
    }
    
    0 讨论(0)
提交回复
热议问题