如何将 JWT 不记名令牌添加到对 API 的所有请求

XN1*_*N16 2 jwt asp.net-core

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

我已经使用 IdS4 正确验证了我的 MVC 项目,从中我得到了一个 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);
}
Run Code Online (Sandbox Code Playgroud)

这是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;
}
Run Code Online (Sandbox Code Playgroud)

StartUp从我收集的内容来看,我的课程非常标准,但如果它们有用,那么我会将它们添加进去。

Ste*_*ary 7

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

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

首先,我建议您使用HttpClientFactory而不是新建一个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);
    }
}
Run Code Online (Sandbox Code Playgroud)

这可以通过 DI 连接:

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

然后您可以将 anHttpClient注入您的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);
Run Code Online (Sandbox Code Playgroud)

这种模式乍一看似乎很复杂,但它将“我如何调用此 API”逻辑与“此操作在做什么”逻辑分开。并且通过 Polly 使用重试/断路器等更容易扩展。