Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/asp.net-core/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何使用IHttpClientFactory刷新令牌_C#_Asp.net Core_Appsettings_Httpclientfactory - Fatal编程技术网

C# 如何使用IHttpClientFactory刷新令牌

C# 如何使用IHttpClientFactory刷新令牌,c#,asp.net-core,appsettings,httpclientfactory,C#,Asp.net Core,Appsettings,Httpclientfactory,我正在使用IHttpClientFactory使用netcore2.2从两个外部api发送请求和接收HTTP响应 我正在寻找一个好的策略,使用存储在appsettings.json中的刷新令牌获取新的访问令牌。当当前请求返回403或401错误时,需要请求新的访问令牌;当获得新的访问和刷新令牌时,需要使用新值更新appsettings.json,以便在后续请求中使用 我使用两个客户端向两个不同的API发送请求,但其中只有一个使用令牌身份验证机制 我已经实现了一些简单的功能,但我正在寻找一个更优雅的

我正在使用IHttpClientFactory使用netcore2.2从两个外部api发送请求和接收HTTP响应

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

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

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

我已在Startup.ConfigureServices方法中注册IHttpClientFactory,如下所示:

services.AddHttpClient();
注册后,我将在两种不同的方法中使用它来调用两种不同的API,第一种方法是:

   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;

        }

    }
公共异步任务GetInformationAsync(AirCallModel模型) { 尝试 { CandidateResults modelCandidateResult=null; var请求=新的HttpRequestMessage(HttpMethod.Get, “https://******/v2/****”; request.Headers.Authorization=newauthenticationHeaderValue(“承载者”、\u appSettings.Value.Token); var clientJAAPI=_httpClientFactory.CreateClient(); var responseclientJAAPI=await-clientJAAPI.sendsync(请求); if(responseclientJAAPI.IsSuccessStatusCode) { modelCandidateResult=await responseclientJAAPI.Content .ReadAsAsync(); .... } if((responseclientJAAPI.StatusCode.ToString()=“未经授权”) { 等待刷新AccessToken(); //再次递归调用此方法 返回等待GetInformationAsync(模型); } 返回null; } 捕获(例外e) { 返回null; } } 刷新令牌方法如下所示:

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;

        }

    }
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"));
专用异步任务RefreshAccessToken()
{
var valuesRequest=新列表();
valuesRequest.Add(新的KeyValuePair(“客户机id”,“****”));
valuesRequest.Add(新的KeyValuePair(“客户机密码”),“****”);
valuesRequest.Add(新的KeyValuePair(“授权类型”、“刷新令牌”);
valuesRequest.Add(新的KeyValuePair(“刷新令牌”,“****”));
RefreshTokenResponse RefreshTokenResponse=null;
var请求=新的HttpRequestMessage(HttpMethod.Post,
“https://******/connect/token”);
request.Content=newformurlencodedcontent(valuesRequest);
var clientJAAPI=_httpClientFactory.CreateClient();
var responseclientJAAPI=await-clientJAAPI.sendsync(请求);
if(responseclientJAAPI.IsSuccessStatusCode)
{
refreshTokenResponse=wait responseclientJAAPI.Content.ReadAsAsAsync();
//这将更新表示配置的POCO对象,但不更新appsettings.json:
_appSettings.Value.Token=refreshTokenResponse.access\u令牌;
}
}
请注意,我正在更新表示配置的POCO对象,而不是appsettings.json,因此新值存储在内存中

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

看起来您需要。简而言之,您可以“截获”http请求并添加授权标头,然后尝试执行它,如果令牌无效,请刷新令牌并重试一次。类似于:

public class AuthenticationDelegatingHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var token = await GetToken();
        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 RefreshToken();
            request.Headers.Authorization = new AuthenticationHeaderValue(token.Scheme, token.AccessToken);
            response = await base.SendAsync(request, cancellationToken);
        }

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

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


更新:

您可以在中找到相同的想法,但由专业人员实施。用法非常简单:

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();
services.AddAccessTokenManagement(选项=>
{
options.Client.Clients.Add(“identityserver”,new ClientCredentialsTokenRequest
{
地址=”https://demo.identityserver.io/connect/token",
ClientId=“m2m.short”,
ClientSecret=“secret”,
Scope=“api”//可选
});
});
services.AddHttpClient(客户端=>
{
client.BaseAddress=新Uri(“https://demo.identityserver.io/api/");
})
.AddClientAccessTokenHandler();

MyClient
发送的请求将始终具有有效的承载令牌。刷新将自动执行。

谢谢。这是一个很好的解决方案。只缺少几件事。首先,我需要在services.AddTransient()之前添加services.AddHttpClient(“name”).AddHttpMessageHandler();因为我收到一个未注册的服务异常。其次,我不喜欢为服务器的每个请求都获取一个新令牌的想法。因为如果在响应发送到客户端后将其保存在内存中,它将被销毁。我需要缓存刷新令牌或类似的东西。你是对的,我忘记了注册,威尔更新我的答案。关于第二点,请看我在最后给出的链接。特别是“AccessTokensCacheManager”及其在“AuthenticationDelegatingHandler”中的用法。它只在需要时重用访问令牌并获取新的。在这里,您可以实现