Blazor Добавление HttpClientHandler для добавления Jwt в заголовок HTTP по запросам

Я использую Visual Studio 2019 а также .Net Core 3.0.0-preview-7 со стандартными шаблонами Blazor Client, Server и Shared.

В приложении наше серверное приложение WebApi всегда будет требовать, чтобы токен JWT присутствовал в заголовке для авторизации.

Смотря на следующее

Делать HTTP-запросы, используя IHttpClientFactory в 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 получить сохраненный токен из localstorage и добавить его в шапку.

Теперь, на данный момент я не уверен, что делать, как будто я добавляю следующее к Blazor.ClientStartup.cs;

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

Я получаю сообщение об ошибке;

"IServiceCollection" не содержит определения для "AddHttpClient", и нет доступного метода расширения "AddHttpClient", принимающего первый аргумент типа "IServiceCollection" (вы пропустили директиву using или ссылку на сборку?)

Кто-нибудь может направить меня к тому, что я делаю здесь неправильно?

4 ответа

Решение

@ Мэтью Флинн, в настоящее время вы не можете использовать IHttpClientFactory на стороне клиента Blazor.

И вам не нужно наследовать от HttpMessageHandler (DelegatingHandler). Это уже было сделано Blazor. Ниже приводится расширение класса n для расширения функциональности службы 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 });
            }
}

Ниже показано, как вызвать конечную точку на вашем веб-интерфейсе, передавая токен Jwt, который читается из localStorage. (кстати, ни одна из этих версий не защищена защитой данных)

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));
    }
}

Надеюсь, что это работает... Если нет, и вы не можете исправить ошибки, приходите сюда и расскажите об этом сообществу...

Я нашел действительно хороший учебник и образец, демонстрирующий это (с утверждениями на основе ролей / политик):https://chrissainty.com/securing-your-blazor-apps-authentication-with-clientside-blazor-using-webapi-aspnet-core-identity/

Ниже приведен отрывок, в котором устанавливаются заголовки запросов по умолчанию для HTTP-клиента по умолчанию (через DI). Все вызовы вашего веб-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")));
    }
    // ...
}

Следующее добавляет заголовок 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.

Вам нужен пакет NuGet Microsoft.Extensions.Http который содержит метод AddHttpClient. Установите его с помощью следующей команды: Install-Package Microsoft.Extensions.Http -Version 3.0.0-preview7.19362.4

Как представляется, этот пакет NuGet автоматически предоставляется в серверном блейзоре, но его необходимо устанавливать отдельно в клиентском блейзоре.

Другие вопросы по тегам