C# 如何使用IHttpClientFactory刷新令牌
我正在使用IHttpClientFactory使用netcore2.2从两个外部api发送请求和接收HTTP响应 我正在寻找一个好的策略,使用存储在appsettings.json中的刷新令牌获取新的访问令牌。当当前请求返回403或401错误时,需要请求新的访问令牌;当获得新的访问和刷新令牌时,需要使用新值更新appsettings.json,以便在后续请求中使用 我使用两个客户端向两个不同的API发送请求,但其中只有一个使用令牌身份验证机制 我已经实现了一些简单的功能,但我正在寻找一个更优雅的解决方案,可以在当前令牌过期时动态更新标头: 我已在Startup.ConfigureServices方法中注册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发送请求,但其中只有一个使用令牌身份验证机制 我已经实现了一些简单的功能,但我正在寻找一个更优雅的
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”中的用法。它只在需要时重用访问令牌并获取新的。在这里,您可以实现