【问题标题】:Blazor Adding HttpClientHandler to add Jwt to HTTP Header on requestsBlazor 添加 HttpClientHandler 以在请求中将 Jwt 添加到 HTTP 标头
【发布时间】:2019-12-27 17:14:41
【问题描述】:

我将 Visual Studio 2019.Net Core 3.0.0-preview-7 与标准 Blazor 客户端、服务器和共享模板一起使用。

在应用程序中,我们的服务器端 WebApi 应用程序将始终要求在标头中存在 JWT 令牌以进行授权。

从下面看

Make HTTP requests using IHttpClientFactory in ASP.NET Core

我创建了以下处理程序;

public class JwtTokenHeaderHandler : DelegatingHandler
{
    private readonly ILocalStorageService _localStorage;

    public JwtTokenHeaderHandler(ILocalStorageService localStorage)
    {
        _localStorage = localStorage;
    }

    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        if (!request.Headers.Contains("bearer"))
        {
            var savedToken = await _localStorage.GetItemAsync<string>("authToken");

            if (!string.IsNullOrWhiteSpace(savedToken))
            {
                request.Headers.Add("bearer", savedToken);
            }
        }

        return await base.SendAsync(request, cancellationToken);
    }
}

我使用Blazored.LocalStorage 从本地存储中获取保存的令牌并将其添加到标题中。

现在,此时我不确定该怎么做,好像我将以下内容添加到Blazor.Client Startup.cs;

services.AddTransient<JwtTokenHeaderHandler>();
services.AddHttpClient("JwtTokenHandler")
    .AddHttpMessageHandler<JwtTokenHeaderHandler>();

我收到错误消息;

“IServiceCollection”不包含“AddHttpClient”的定义 并且没有可访问的扩展方法“AddHttpClient”接受第一个 可以找到“IServiceCollection”类型的参数(您是否缺少 using 指令还是程序集引用?)

谁能指导我在这里做错了什么?

【问题讨论】:

  • @HenkHolterman 我说这是在Blazor.Client 项目中?我想将 JWT 添加到我对服务器端控件进行的所有调用的标头中。
  • 托管模型/模板是 Blazor 客户端。加上一个“普通”的 Web Api 项目。
  • @Matthew Flynn,你最好调用你的应用客户端 Blazor。
  • @HenkHolterman 好的,所以在“普通”webapi 上我在控制器上有[Authorize] 属性,授权是消息头中的标准“承载”jwt。这工作正常,现在在 blazor 客户端应用程序上,当它调用以获取一些数据等到 WebApi 时,我只想拦截 Post、Get 等并将存储在 localstorage 中的 Jwt 添加到请求的标头中跨度>
  • 我觉得这个网址对你有帮助docs.microsoft.com/en-us/aspnet/core/blazor/…

标签: c# asp.net-core blazor blazor-client-side


【解决方案1】:

@Matthew Flynn,目前您不能在客户端 Blazor 上使用 IHttpClientFactory。

而且你不必从 HttpMessageHandler (DelegatingHandler) 派生。 Blazor 已经完成了这项工作。下面是一个扩展类,用于扩展 HttpClient 服务的功能,以便能够将 Jwt 令牌添加到请求消息的标头中...

ServiceExtensions.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net.Http;
    using System.Net.Http.Headers;
    using System.Security.Claims;
    using System.Text;
    using System.Text.Json.Serialization;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Components;
    using Microsoft.Extensions.DependencyInjection;

 public static class ServiceExtensions
    {    
     public static async Task<T> GetJsonAsync<T>(this HttpClient httpClient, string url, AuthenticationHeaderValue authorization)
            {
                var request = new HttpRequestMessage(HttpMethod.Get, url);
                request.Headers.Authorization = authorization;

                var response = await httpClient.SendAsync(request);
                var responseBytes = await response.Content.ReadAsByteArrayAsync();
                return JsonSerializer.Parse<T>(responseBytes, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
            }
}

下面展示了如何调用 Web Api 上的端点,传递从 localStorage 读取的 Jwt 令牌。 (顺便说一下,这些版本都没有数据保护)

Index.razor

@page "/"

@inject ILocalStorageService localStorage
@inject HttpClient Http

<div class="mdc-card main-content-card">
    <h1 class="@MdcTypography.H4">Hello, world!</h1>

    Welcome to your new app.
</div>

// Razor content to display emloyees come here.....

@code {
Employee[] employees;

    protected override async Task OnInitAsync()
    {
        var token = await localStorage.GetTokenAsync();
        employees = await Http.GetJsonAsync<Employee[]>(
            "api/employees",
            new AuthenticationHeaderValue("Bearer", token));
    }
}

希望这可行...如果没有,并且您无法解决错误,请来这里告诉社区...

【讨论】:

  • 我同意 Blazor 似乎限制了这一点,因为它添加了自己的 HttpClient 默认值以使用 webassembly(我确实有一个 gitu 问题链接但找不到它)。我想扩展方法是目前最好的解决方法,我只是希望我可以避免拦截每个调用而不必这样做。 :(
  • 知道这一点的人不多,但是客户端 Blazor 上的 HttpClient 服务并不是我们多年来所知道的实际或真正的 HttpClient。它实际上是 JavaScript Fetch Api 的一个实现...
  • 跟进这个,我可以在扩展方法中从localstorage中抽象出token吗?我试图制作一个 HttpClient 包装器来减少组件端的代码,这些代码确实在一个“PostJsonAsync()”调用中添加了授权标头和令牌值
【解决方案2】:

我找到了一个非常好的教程和示例来演示这一点(完整的基于角色/策略的声明): https://chrissainty.com/securing-your-blazor-apps-authentication-with-clientside-blazor-using-webapi-aspnet-core-identity/

这是下面的摘录,在默认 http 客户端(通过 DI)上设置默认请求标头。然后,对您的 Web api 的所有调用都将包含不记名令牌:

public class ApiAuthenticationStateProvider : AuthenticationStateProvider
{
    private readonly HttpClient _httpClient;
    private readonly ILocalStorageService _localStorage;

    public ApiAuthenticationStateProvider(HttpClient httpClient, ILocalStorageService localStorage)
    {
        _httpClient = httpClient;
        _localStorage = localStorage;
    }
    public override async Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        var savedToken = await _localStorage.GetItemAsync<string>("authToken");

        if (string.IsNullOrWhiteSpace(savedToken))
        {
            return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
        }

        // **************    Set JWT header       ****************
        _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", savedToken);
        // *******************************************************

        return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity(ParseClaimsFromJwt(savedToken), "jwt")));
    }
    // ...
}

【讨论】:

    【解决方案3】:

    您需要 NuGet 包Microsoft.Extensions.Http,其中包含 AddHttpClient 方法。使用以下命令安装它:Install-Package Microsoft.Extensions.Http -Version 3.0.0-preview7.19362.4

    看起来,这个 NuGet 包是在服务器端 blazor 中自动提供的,但必须在客户端 blazor 中单独安装。

    【讨论】:

    • 正确。这是一个大(gish)包,必须尽可能地修剪 wasm 运行时。
    • @HenkHolterman 从链接中我发现它看起来与 wasm 不兼容,因为 blazor 客户端设置了自己的 httpclient,覆盖它似乎会破坏它。将此安装到客户端项目然后运行它当前会导致运行时异常。
    • @MatthewFlynn - 看看确切的名称,Blazor 有自己的 HttpClient 包。
    【解决方案4】:

    以下将 X-CSRF-TOKEN 标头添加到 http 请求中:

    public class CustomHttpMessageHandler : DelegatingHandler
    {
        private readonly IJSRuntime _js;
        public CustomHttpMessageHandler(IJSRuntime js)
        {
            _js = js;
        }
    
        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            var afrt = await _js.InvokeAsync<string>("getCookie", ".AFRT");
            request.Headers.Add("X-CSRF-TOKEN", afrt);
            return await base.SendAsync(request, cancellationToken);
        }
    }
    

    在 Program.cs 中配置如下:

    builder.Services.AddScoped<CustomHttpMessageHandler>();
    builder.Services.AddHttpClient("ApiClient", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
        .AddHttpMessageHandler<CustomHttpMessageHandler>();
    builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("ApiClient"));
    

    您需要将 Microsoft.Extensions.Http 包安装到您的 blazor webassembly 客户端。

    【讨论】:

      猜你喜欢
      • 2017-02-15
      • 2012-12-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多