如何将 JWT 承载令牌添加到 API 的所有请求

2024-05-01

我正在尝试组建一个使用 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;
}

My StartUp从我收集的数据来看,类是相当标准的,但如果它们有用,那么我会将它们添加进去。


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

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

首先,我推荐你使用HttpClientFactory而不是新建一个HttpClient https://learn.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests#issues-with-the-original-httpclient-class-available-in-net-core.

接下来,添加授权标头是 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,通过重试/断路器等更容易扩展。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何将 JWT 承载令牌添加到 API 的所有请求 的相关文章

随机推荐