Пользовательские утверждения всегда пусты при вызове API из серверного приложения Blazor

#c# #asp.net-core #blazor-server-side

#c# #asp.net-core #blazor-на стороне сервера

Вопрос:

У меня есть проект .NET Core, в котором размещены WebAPI и серверное приложение Blazor. Приложение использует аутентификацию и авторизацию по умолчанию через an IdentityDbContext и использует значение по умолчанию AuthenticationStateProvider для Blazor. Поскольку я хочу, чтобы это приложение в какой-то момент в будущем могло перейти на клиентский Blazor, я сохранил логику на стороне сервера в API и вызывал ее, через HttpClient которую я вручную ввел при запуске с помощью services.AddScoped<HttpClient>();

Проблема, с которой я сталкиваюсь, заключается в том, что пользовательские утверждения (т. Е. HttpContext.User ) Пусты в контроллерах API при выполнении вызовов API. Когда я говорю empty, HttpContext.User объект не равен null, но все значения по умолчанию. Ожидаемое поведение заключается в том, что утверждения будут заполнены идентификационной информацией для вошедшего в систему пользователя.

Как ни странно, идентификационный файл cookie .AspNetCore.Identity.Application присутствует в браузере, и все значения в AuthenticationStateProvider сервисе соответствуют ожидаемым для вошедшего в систему пользователя. Для меня это указывает на то, что все настроено правильно с точки зрения аутентификации и идентификации и что проблема каким-то образом связана с HttpClient контроллерами API.

Я пробовал:

  • Установка HttpOnly значения false в конфигурации файлов cookie и извлечение файла cookie вручную через JsInterop для прикрепления HttpClient заголовков. Это не удалось из-за того, что вызовы API начинались до того, как JsInterop закончил захват файлов cookie.
  • Вручную вводим HttpClient в мой ApiService via services.AddHttpClient<IApiService, ApiService>();
  • Копирование конфигурации запуска из рабочего проекта и перезапись моего
  • Изменение порядка настроенных служб различными способами
  • Несколько разных конфигураций файлов cookie
  • Создание и использование пользовательского AuthenticationStateProvider
  • Следуя нескольким различным руководствам из официальной документации Microsoft
  • Просмотр бесчисленных статей и вопросов Stackoverflow по аналогичным вопросам и проверка их предложений

Я также попытался вручную добавить файл cookie аутентификации в с HttpClient помощью следующего:

 services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddScoped<HttpClient>(s =>
{
    var httpContextAccessor = s.GetRequiredService<IHttpContextAccessor>();
    string authToken = httpContextAccessor.HttpContext.Request.Cookies[".AspNetCore.Identity.Application"];

    var client = new HttpClient(new HttpClientHandler
    {
        UseCookies = false
    });

    if (authToken != null)
    {
        client.DefaultRequestHeaders.Add("Cookie", $".AspNetCore.Identity.Application={authToken}");
    }

    var request = httpContextAccessor.HttpContext.Request;
    client.BaseAddress = new Uri($"{request.Scheme}://{request.Host}/");
    return client;
});
  

Это решение не удалось, так как httpContextAccessor при вызове делегата возвращалось значение null. Я понимаю, что нет HttpContext с подключением SignalR, однако я все равно ожидал бы HttpContext при первой загрузке приложения, обновлении страницы или вызове API. Я также попробовал нулевую версию проверки, где она будет выполняться только в том случае, если HttpContext значение не равно нулю, но это также не удалось, поскольку HttpContext никогда не было ненулевым.

Ответ №1:

Я смог решить эту проблему, передав токен аутентификации непосредственно в приложение Blazor, а затем добавив его HttpClient таким образом.

Хотя мое приложение размещено в представлении MVC, я понимаю, что наиболее распространенный способ размещения приложения Blazor — это страница Razor. Для всех, кто еще сталкивается с этой проблемой, изменение страницы Razor будет следующим.

_Host.cshtml

 @{
    var token = HttpContext.Request.Cookies[".AspNetCore.Identity.Application"];
}

<app>
    <component type="typeof(App)" param-Token="token" render-mode="ServerPrerendered" />
</app>
  

Решение MVC включает в себя получение токена на уровне контроллера и передачу его вниз. Код для этого следующий.

HomeController.cs

 [HttpGet]
public IActionResult Index()
{
    var token = HttpContext.Request.Cookies[".AspNetCore.Identity.Application"];
    return View(token);
}
  

Index.cshtml

 @model string

...

<app>
    <component type="typeof(App)" param-Token="@Model" render-mode="ServerPrerendered" />
</app>
  

И, наконец, независимо от того, какой метод использовался выше, приложение Blazor будет изменено, чтобы добавить токен к HttpClient следующему.

App.razor

 @inject HttpClient HttpClient

...

@code
{
    [Parameter] public string Token { get; set; }

    protected override void OnInitialized()
    {
        base.OnInitialized();
        HttpClient.DefaultRequestHeaders.Add("Cookie", $".AspNetCore.Identity.Application={Token}");
    }
}