在另一个 Web API 中管理 Web API JWT 令牌的最佳实践

G.D*_*mov 7 c# authentication jwt asp.net-web-api .net-framework-4.8

我的项目:

Web API 项目 - ASP .NET Framework 4.8

问题?

代码流程如下:

1.) 调用 API -> 它必须调用另一个 API -> 2.) 获取 JWT 身份验证令牌 -> 3.) 调用所需的方法。

问题是,如果我的 API 被调用 100 次,我将对该GetJwtToken()方法进行 100 次调用,并为所需的方法本身再进行 100 次调用,这似乎是身份验证服务器上的开销。令牌本身的寿命为 2 小时。

关于如何在另一个 Web API 中管理 Web API JWT 令牌,是否有任何记录在案的最佳实践?

我尝试了什么?

我已经尝试了以下解决方案,但我仍然不确定它们是否可以被视为良好做法。

  • 一个静态类有两个静态属性TokenValidTo与一个静态方法GetJwtToken()的更新这些属性。在每次调用所需的外部 API 方法之前,我们检查ValidTo属性并Token通过静态方法更新值(如果它已过期)。
  • 在我们的服务中,我们有一个静态私有字段Token。调用外部 API 方法的方法被一个try catch块包围。该 Catch(WebException ex)预计如果令牌已过期未授权的例外。我检查 HTTP 状态代码 401 - 未经授权。
if (response.StatusCode == HttpStatusCode.Unauthorized)
Run Code Online (Sandbox Code Playgroud)

如果我们进入该if子句,我们Token通过调用块GetJwtToken()内的方法catch然后再次递归调用该方法来更新属性。通过这种方式,我们仅在令牌过期并引发未经授权的异常时才更新令牌。

  • 我得到但没有测试的另一个想法是ActionFilterAttribute使用覆盖OnActionExecuting(HttpActionContext actionContext)方法。在我们进入 Web API 控制器之前,action 属性已经检查了我们Token是否已经过期。这里的问题是我不确定在哪里保存Token财产。可能作为另一个类中的静态值。

是否有其他方法可以在另一个 Web API 中管理 Web API 的 JWT 令牌,以及什么被认为是最佳实践?
一些代码片段、伪代码或文章将不胜感激。


Edit1:
我读过这个问题,但它对我没有帮助,因为它是关于如何管理前端部分的令牌。这里的项目是 Web API,它都在服务器端。
Edit2:
在这里和那里编辑了一些句子,使其更具可读性。
Edit3:
添加了一个我想到的选项。

zhu*_*ber 2

我会以某种方式处理这个问题BaseApiService

public class BaseApiService
{
    private readonly IHttpClientFactory httpClientFactory;
    private readonly ITokenHandler tokenHandler;

    public BaseApiService(IHttpClientFactory httpClientFactory, ITokenHandler tokenHandler)
    {
        this.httpClientFactory = httpClientFactory;
        this.tokenHandler = tokenHandler;
    }

    protected async Task<HttpResponseMessage> RequestAsync(HttpRequestMessage requestMessage)
    {
        var httpClient = httpClientFactory.CreateClient();
        requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokenHandler.Token);
        var response = await httpClient.SendAsync(requestMessage);
        
        if (!response.IsSuccessStatusCode)
        {
            if (response.StatusCode == HttpStatusCode.Unauthorized)
            {
                var token = await tokenHandler.UpdateTokenAsync();
                requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
                return await RequestAsync(requestMessage);
            }
        }

        return response;
    }
}
Run Code Online (Sandbox Code Playgroud)

它将负责发出请求、响应序列化(请注意,为了简单起见,我使用了字符串响应)并处理每个请求的令牌。此外,您可能需要考虑处理错误并处理无限循环,因为它当前正在调用 self (例如,在第二次调用时,如果再次未经授权,则错误退出)。

令牌处理程序将在 DI 中定义为单例,这是实现

public interface ITokenHandler
{
    string Token { get; }
    Task<string> UpdateTokenAsync();
}

public class TokenHandler : ITokenHandler
{
    private readonly IHttpClientFactory httpClientFactory;
    public string Token { get; private set; } 

    public TokenHandler(IHttpClientFactory httpClientFactory)
    {
        this.httpClientFactory = httpClientFactory;
    }
    
    public async Task<string> UpdateTokenAsync()
    {
        var httpClient = httpClientFactory.CreateClient();
        var result = await httpClient.PostAsync("/external-api/token", new FormUrlEncodedContent(new []
        {
            new KeyValuePair<string, string>("username", "external-admin"),
            new KeyValuePair<string, string>("password", "external-password"),
        }));

        // or handle it however you want
        var token = result.IsSuccessStatusCode
            ? await result.Content.ReadAsStringAsync()
            : null;

        if (!String.IsNullOrEmpty(token))
        {
            Token = token;
        }

        return Token;
    }
}
Run Code Online (Sandbox Code Playgroud)

这就是你消费你的方式BaseApiService

public class TodoService : BaseApiService
{
        public TodoService(IHttpClientFactory httpClientFactory, ITokenHandler tokenHandler) 
        : base(httpClientFactory, tokenHandler)
    {
    }

    public async Task<string> GetTodoAsync(int id)
    {
        var response = await RequestAsync(new HttpRequestMessage(HttpMethod.Get, $"/todo/{id}"));
        return await response.Content.ReadAsStringAsync();
    }
}
Run Code Online (Sandbox Code Playgroud)

我认为您不需要添加任何ValidTo逻辑,而只需依赖Unauthorized来自第 3 方 API 的响应,因为您只会使代码复杂化,而且无论如何您都必须处理Unauthorized响应。

唯一的问题是您可能lock会从 获取/设置令牌TokenHandler,但这只是一个基本示例,用于展示我如何实现它。