【问题标题】:How to add JWT Bearer Token to ALL requests to an API如何将 JWT 不记名令牌添加到对 API 的所有请求
【发布时间】:2020-09-30 23:30:58
【问题描述】:

我正在尝试组建一个使用 Asp.Net Core Identity、Identity Server 4 和 Web API 项目的小项目。

我的 MVC 项目使用 IdS4 正确进行身份验证,我从中获得了一个 JWT,然后我可以将它添加到我的 Web API 项目的请求的标头中,这一切都按预期工作。

我遇到的问题是我实际上是如何将令牌添加到HttpClient,基本上我为每个明显错误的请求设置它,否则我会在网上看到其他示例,但我没有无法确定重构它的好方法。我已经阅读了很多文章,但我发现关于这部分流程的信息很少,所以我猜测它可能非常简单,以至于指南中从未详细说明,但我仍然不知道!

这是一个调用我的 API 的示例 MVC 操作:

[HttpGet]
[Authorize]
public async Task<IActionResult> GetFromApi()
{
    var client = await GetHttpClient();
    string testUri = "https://localhost:44308/api/TestItems";
    var response = await client.GetAsync(testUri, HttpCompletionOption.ResponseHeadersRead);
    var data = await response.Content.ReadAsStringAsync();

    GetFromApiViewModel vm = new GetFromApiViewModel()
    {
        Output = data
    };
    return View(vm);
}

这是我调用的GetHttpClient() 方法(当前位于同一个控制器中):

private async Task<HttpClient> GetHttpClient()
{
    var client = new HttpClient();
    var expat = HttpContext.GetTokenAsync("expires_at").Result;
    var dataExp = DateTime.Parse(expat, null, DateTimeStyles.RoundtripKind);
    if ((dataExp - DateTime.Now).TotalMinutes < 10)
    {
         //SNIP GETTING A NEW TOKEN IF ITS ABOUT TO EXPIRE
    }

    var accessToken = await HttpContext.GetTokenAsync("access_token");
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);

    return client;
}

我的StartUp 类在我收集的内容中非常标准,但如果它们有用,那么我会添加它们。

【问题讨论】:

    标签: asp.net-core jwt


    【解决方案1】:

    我已经阅读了很多文章,但我发现关于这部分流程的信息很少,所以我猜测它可能非常简单,以至于指南中从未详细说明,但我仍然不知道!

    问题在于文档确实遍布各处,因此很难全面了解所有最佳实践。我正在计划一个关于“现代 HTTP API 客户端”的博客系列,它将收集所有这些最佳实践。

    首先,我recommend you use HttpClientFactory rather than new-ing up an HttpClient

    接下来,IMO 最好通过连接到HttpClient 的消息处理程序管道来添加授权标头。一个基本的不记名令牌身份验证助手可能如下所示:

    public sealed class BackendApiAuthenticationHttpClientHandler : DelegatingHandler
    {
        private readonly IHttpContextAccessor _accessor;
    
        public BackendApiAuthenticationHttpClientHandler(IHttpContextAccessor accessor)
        {
            _accessor = accessor;
        }
    
        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            var expat = await _accessor.HttpContext.GetTokenAsync("expires_at");
            var dataExp = DateTime.Parse(expat, null, DateTimeStyles.RoundtripKind);
            if ((dataExp - DateTime.Now).TotalMinutes < 10)
            {
                 //SNIP GETTING A NEW TOKEN IF ITS ABOUT TO EXPIRE
            }
    
            var token = await _accessor.HttpContext.GetTokenAsync("access_token");
    
            // Use the token to make the call.
            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
            return await base.SendAsync(request, cancellationToken);
        }
    }
    

    这可以通过 DI 连接:

    services.AddTransient<BackendApiAuthenticationHttpClientHandler>();
    services.AddHttpClient<MyController>()
        .ConfigureHttpClient((provider, c) => c.BaseAddress = new Uri("https://localhost:44308/api"))
        .AddHttpMessageHandler<BackendApiAuthenticationHttpClientHandler>();
    

    然后您可以将HttpClient 注入您的MyController,它会神奇地使用身份验证令牌:

    // _client is an HttpClient, initialized in the constructor
    string testUri = "TestItems";
    var response = await _client.GetAsync(testUri, HttpCompletionOption.ResponseHeadersRead);
    var data = await response.Content.ReadAsStringAsync();
    
    GetFromApiViewModel vm = new GetFromApiViewModel()
    {
        Output = data
    };
    return View(vm);
    

    这个模式起初看起来很复杂,但是它将“我如何调用这个 API”逻辑与“这个动作在做什么”逻辑分开。并且通过 Polly 更容易扩展重试/断路器等。

    【讨论】:

    • 感谢您的示例,这正是我所需要的。一旦我创建了 HttpClientFactory 并设法将其注入我的 Api 类和 BackendApiAuthenticationHttpClientHandler 本身,一切都运行顺利。我一定会留意你关于这个主题的博客文章,因为我相信它会回答我关于这个主题的许多其他问题!
    • 你写过那篇博文吗?
    • @Fredou:不。我们今年早些时候搬家了,现在又搬家了。大概在 2022 年……
    【解决方案2】:

    您可以使用HttpRequestMessage

    // Create this instance once on stratup 
    // (preferably you want to keep an instance per base url to avoid waiting for socket fin)
    HttpClient client = new HttpClient();
    

    然后创建HttpRequestMessage的实例:

    HttpRequestMessage request = new HttpRequestMessage(
      HttpMethod.Get, 
      "https://localhost:44308/api/TestItems");
    request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "ey..");
    await client.SendAsync(request);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-07
      • 2018-04-09
      • 2020-12-11
      • 2020-02-21
      • 2019-08-17
      • 2016-03-17
      • 2019-03-23
      • 2014-06-19
      相关资源
      最近更新 更多