如何使用 IHttpClientFactory 刷新令牌 [英] How to Refresh a token using IHttpClientFactory

查看:26
本文介绍了如何使用 IHttpClientFactory 刷新令牌的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用 IHttpClientFactory 来发送请求和接收来自两个使用 Net Core 2.2 的外部 API 的 HTTP 响应.

I am using IHttpClientFactory for sending requests and receiving HTTP responses from two external APIs using Net Core 2.2.

我正在寻找一种使用已存储在 appsettings.json 中的刷新令牌来获取新访问令牌的好策略.当前请求返回 403 或 401 错误时需要请求新的访问令牌,当获得新的访问和刷新令牌时,需要使用新值更新 appsettings.json 以便在后续请求中使用.

I am looking for a good strategy to get a new access token using a refresh token that has been stored in the appsettings.json. The new access token needs to be requested when the current request returns 403 or 401 errors, When the new access and refresh token have been obtained, the appsettings.json needs to be updated with the new values in order to be used in subsequent requests.

我使用两个客户端向两个不同的 API 发送请求,但其中只有一个使用令牌身份验证机制.

I am using two clients to send requests to two different APIs but only one of them use token authentication mechanism.

我已经实现了一些简单有效的方法,但我正在寻找一种更优雅的解决方案,它可以在当前令牌过期时动态更新标头:

I have implemented something simple that works but i am looking for a more elegant solution that can update the header dynamically when the current token has expired :

我已经在 Startup.ConfigureServices 方法中注册了 IHttpClientFactory 如下:

I have registered the IHttpClientFactory in the Startup.ConfigureServices method as follows:

services.AddHttpClient();

注册后,我以两种不同的方法使用它来调用两个不同的 API,第一种方法是:

Once registered i am using it in two different methods to call two different APIs, the first method is:

   public async Task<AirCallRequest> GetInformationAsync(AirCallModel model)
    {
        try
        {


            CandidateResults modelCandidateResult = null;

            var request = new HttpRequestMessage(HttpMethod.Get,
            "https://*******/v2/*****");
            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _appSettings.Value.Token);


            var clientJAAPI = _httpClientFactory.CreateClient();
            var responseclientJAAPI = await clientJAAPI.SendAsync(request);


            if (responseclientJAAPI.IsSuccessStatusCode)
            {
                modelCandidateResult = await responseclientJAAPI.Content
                   .ReadAsAsync<CandidateResults>();

                ....
            }


            if ((responseclientJAAPI .StatusCode.ToString() == "Unauthorized")
            {                    

                await RefreshAccessToken();

               //Calls recursively this method again
                return await GetInformationAsync(model);

            }

            return null;
        }
        catch (Exception e)
        {
            return null;

        }

    }

refresh Token 方法如下所示:

The refresh Token method looks like that:

private async Task RefreshAccessToken()
    {


        var valuesRequest = new List<KeyValuePair<string, string>>();
        valuesRequest.Add(new KeyValuePair<string, string>("client_id", "*****"));
        valuesRequest.Add(new KeyValuePair<string, string>("client_secret","****"));
        valuesRequest.Add(new KeyValuePair<string, string>("grant_type", "refresh_token"));
        valuesRequest.Add(new KeyValuePair<string, string>("refresh_token", "*****"));


        RefreshTokenResponse refreshTokenResponse = null;

        var request = new HttpRequestMessage(HttpMethod.Post,
        "https://*****/connect/token");

        request.Content = new FormUrlEncodedContent(valuesRequest);

        var clientJAAPI = _httpClientFactory.CreateClient();
        var responseclientJAAPI = await clientJAAPI.SendAsync(request);

        if (responseclientJAAPI.IsSuccessStatusCode)
        {
            refreshTokenResponse = await responseclientJAAPI.Content.ReadAsAsync<RefreshTokenResponse>();

            //this updates the POCO object representing the configuration but not the appsettings.json :
            _appSettings.Value.Token = refreshTokenResponse.access_token;

        }

    }

请注意,我正在更新代表配置的 POCO 对象,而不是 appsettings.json,因此新值存储在内存中.我想为后续请求更新 appsettings.json.

Notice that I am updating the POCO object representing the configuration but not the appsettings.json, so the new values are stored in memory. I want to update the appsettings.json for subsequent requests.

如果建议的解决方案需要在 Startup.ConfigureService 中定义 Httpclient 的主要设置,则需要允许创建 HttpClien 的不同实例,因为其中一个 HttpClient 实例(在另一种方法中使用以调用第二个 API) 不需要令牌来发送请求.

If the solution proposed require to define the main settings for the Httpclient in the Startup.ConfigureService, it needs to allow to create different instances of the HttpClien, because one of the HttpClient instances (use in another method to call a second API) doesn't require a token to send the requests.

推荐答案

看起来你需要 DelegatingHandler.用两个词你可以拦截"您的 http 请求并添加 Authorization 标头,然后尝试执行它,如果令牌无效,请刷新令牌并再试一次.类似的东西:

Looks like you need DelegatingHandler. In two words you can "intercept" your http request and add the Authorization header, then try to execute it and if token was not valid, refresh token and retry one more time. Something like:

public class AuthenticationDelegatingHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var token = await GetTokenAsync();
        request.Headers.Authorization = new AuthenticationHeaderValue(token.Scheme, token.AccessToken);
        var response = await base.SendAsync(request, cancellationToken);

        if (response.StatusCode == HttpStatusCode.Unauthorized || response.StatusCode == HttpStatusCode.Forbidden)
        {
            token = await RefreshTokenAsync();
            request.Headers.Authorization = new AuthenticationHeaderValue(token.Scheme, token.AccessToken);
            response = await base.SendAsync(request, cancellationToken);
        }

        return response;
    }
}

您可以像这样在 Startup.cs 中注册这个委托处理程序:

You register this delegating handler in Startup.cs like that:

services.AddTransient<AuthenticationDelegatingHandler>();
services.AddHttpClient("MySecuredClient", client =>
    {
        client.BaseAddress = new Uri("https://baseUrl.com/");
    })
    .AddHttpMessageHandler<AuthenticationDelegatingHandler>();

然后这样使用:

var securedClient = _httpClientFactory.CreateClient("MySecuredClient");
securedClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, "v2/relativeUrl"));

关于在 appsetting.json 中存储刷新令牌.我认为这不是一个好主意,因为刷新令牌没有过期时间.如果您第一次可以使用凭据获取新令牌,请使用它,然后将刷新令牌存储在内存中以供进一步刷新.

Regarding storing refresh token in appsetting.json. I don't think it's a good idea as refresh token doesn't have expiration time. If you can use credentials to obtain new token for the first time, use it, and then store refresh token in-memory for further refreshes.

此处您可以看到我如何管理客户端凭据令牌刷新并尝试使其适用于你的场景.

Here you can see how I manage client credential token refreshes and try to make it work for your scenario.

更新:

在这里您可以找到相同的想法,但由专业人士实施并可用在 nuget 中.用法很简单:

Here you can find same idea but implemented by professionals and available in nuget. The usage is very simple:

services.AddAccessTokenManagement(options =>
{
    options.Client.Clients.Add("identityserver", new ClientCredentialsTokenRequest
    {
        Address = "https://demo.identityserver.io/connect/token",
        ClientId = "m2m.short",
        ClientSecret = "secret",
        Scope = "api" // optional
    });
});

services.AddHttpClient<MyClient>(client =>
{
    client.BaseAddress = new Uri("https://demo.identityserver.io/api/");
})
.AddClientAccessTokenHandler();

MyClient 发送的请求将始终具有有效的不记名令牌.刷新自动执行.

Requests sent by MyClient will always have valid bearer token. The refresh performed automatically.

这篇关于如何使用 IHttpClientFactory 刷新令牌的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆