c# - 在另一个 Web API 中管理 Web API JWT token 的最佳实践

标签 c# authentication asp.net-web-api jwt .net-framework-4.8

我的项目:
Web API 项目 - ASP .NET Framework 4.8
问题?
代码流程如下:
1.) API 被调用 -> 它必须调用另一个 API -> 2.) 获取 JWT 身份验证 token -> 3.) 调用所需的方法。
问题是如果我的 API 被调用 100 次,我将为 GetJwtToken() 调用 100 次。方法和另一个 100 用于所需方法本身,这似乎是身份验证服务器的开销。 token 本身的生命周期为 2 小时。
是否有关于如何在另一个 Web API 中管理 Web API JWT token 的最佳实践记录?
我尝试了什么?
我已经尝试了以下解决方案,但我仍然不确定它们是否可以被视为良好做法。

  • 一个具有两个静态属性的静态类 TokenValidTo和一种静态方法GetJwtToken()更新这些属性。在每次调用所需的外部 API 方法之前,我们检查 ValidTo属性并更新 Token如果它已过期,则通过静态方法获取值。
  • 在我们的服务中,我们有一个静态私有(private)字段 Token .调用外部API方法的方法用try包围catch block 。 Catch(WebException ex)如果 token 已过期,则预计会出现未经授权的异常。我检查 HTTP 状态代码 401 - 未经授权。

  • if (response.StatusCode == HttpStatusCode.Unauthorized)
    
    如果我们进入 if子句我们更新 Token属性通过调用 GetJwtToken() catch 中的方法阻塞,然后再次递归调用该方法。通过这种方式,我们仅在 token 过期并且引发未经授权的异常时才更新 token 。
  • 我得到但没有测试的另一个想法是ActionFilterAttribute被覆盖 OnActionExecuting(HttpActionContext actionContext)方法。在我们进入 Web API Controller 之前,action 属性已经检查了我们是否有 Token。如果它已经过期。这里的问题是我不确定在哪里保存 Token属性(property)。可能作为另一个类中的静态值。

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

    编辑1:
    我已阅读 this问题,但这对我没有帮助,因为它是关于如何管理前端部分的 token 。这里的项目是 Web API,它都在服务器端。
    编辑2:
    在这里和那里编辑了一些句子,所以它更具可读性。
    编辑3:
    添加了一个我想到的选项。

    最佳答案

    我会用某种 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;
        }
    }
    
    它将负责发出请求、响应序列化(注意,为了简单起见,我使用了字符串响应)和处理每个请求的 token 。此外,您可能需要考虑处理错误并处理无限循环,因为它当前正在调用 self (例如,如果再次未经授权,则在第二次调用时退出并出现错误)。
    token 处理程序将在 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;
        }
    }
    
    这就是您使用 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();
        }
    }
    
    我认为您不需要添加任何 ValidTo逻辑,但只依靠你的Unauthorized来自第 3 方 API 的响应,因为您只会使代码复杂化并且必须处理 Unauthorized无论如何回应。
    唯一的问题是你可能会lockTokenHandler 获取/设置 token ,但这只是一个基本示例,展示了我将如何实现它。

    关于c# - 在另一个 Web API 中管理 Web API JWT token 的最佳实践,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64063083/

    相关文章:

    c# - 为什么异步方法不立即返回可等待对象?

    authentication - REST 和身份验证变体

    javascript - 如何访问 ember-simple-auth 中的 session 属性?

    authentication - 当有人以我身份运行 Google Apps Script Web 应用程序时获取用户信息

    c# - 在 webapi 中使用 OData 获取仅在运行时已知的属性

    c# - 从 C# 调用 C 函数,传递包含指针的结构

    c# - 为什么计数器在线程池中不起作用

    c# - 尝试将长字符串提交给 Asp.Net Web Api 2 Controller 方法时出现错误请求

    c# - 使用 asp.net core 3.0 时传递有效 token 时,Postman 返回 401 unothorized

    c# - RC 更新后的 ASP.NET WebAPI XML 序列化