对于我的应用程序中的每个 API 调用,我想检查用户是否有过期的 JWT,如果是,我想使用刷新令牌获取一个新的,然后继续对 API 的原始请求。这应该全部在后台工作,APP用户不会遇到任何中断或需要再次登录。
我像这样创建我的 HttpClient:
static DelegatingHandler handler = new AuthenticationHandler();
static HttpClient httpClient = new HttpClient(handler)
{
BaseAddress = new Uri("https://10.0.2.2:5001/api/v1")
};
AuthenticationHandler 是一个自定义的 DelegatingHandler,它有一个覆盖SendAsync
方法。在该方法中,我检查请求是否具有状态Unauthorised
:
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
如果是这样,我需要使用当前拥有的 JWT 和 Refresh 令牌向我的 API 发送另一个请求以生成新的令牌对......因为这是另一个 API 调用中间的 API 调用(因为这一切都发生在内部自定义 DelegatingHandler 是构造我的主 HttpClient 的参数) - 是否需要使用我需要创建的第二个 HttpClient 来刷新令牌以进行刷新令牌调用?
我看不出如何为此使用相同的 HttpClient,这通常是如何完成的?
编辑:
我看不到如何从 AuthenticationHandler 内部使用相同的 HttpClient 进行 refreshToken 调用,因为处理程序用于构造 HttpClient。感觉像是循环引用。我只是不知道其他人如何在他们的代码中做到这一点......我目前通过使用第二个 HttpClient 来实现它,我只用于那个refreshToken
调用,它可以工作,但我觉得有一种更清洁的方法来实现这个?
顺便说一句,我(尚未重构) AuthenticationHandler 中的 SendAsync 方法目前看起来像这样:
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
try
{
HttpResponseMessage response = new HttpResponseMessage();
request = CheckForAuthToken(request);
response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
for (int i = 1; i == _maxRefreshAttempts; i++)
{
// Here I make a call to the API to refresh and return a new JWT, The authApiService uses a different HttpClient
RefreshTokenRequestModel refreshTokenRequestModel = new RefreshTokenRequestModel
{
Token = await SecureStorage.GetAsync("jwtToken"),
RefreshToken = await SecureStorage.GetAsync("refreshToken")
};
var apiResponse = await authApiService.RefreshToken(refreshTokenRequestModel);
if (apiResponse.IsSuccessStatusCode)
{
await SecureStorage.SetAsync("jwtToken", apiResponse.Content.Token);
await SecureStorage.SetAsync("refreshToken", apiResponse.Content.RefreshToken);
request = CheckForAuthToken(request);
response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
}
}
}
return response;
}
catch (Exception e)
{
throw e;
}
}