Контекст пользователя в сервисах (UserContext) и проброс токена между сервисами

↑ 3.4 Аутентификация и авторизация · ← Предыдущая · → Следующая

Обязательно~3 мин чтенияУровень: middle

Зачем это на собесе

Практика микросервисов: как сервис узнаёт «текущего пользователя» и как вызвать другой сервис от его имени.

Объяснение

Не тащите HttpContext в бизнес-логику. Оберните доступ к пользователю интерфейсом.

public interface IUserContext { Guid UserId { get; } bool IsAdmin { get; } }
 
public class HttpUserContext(IHttpContextAccessor accessor) : IUserContext
{
    private ClaimsPrincipal User => accessor.HttpContext?.User ?? throw new InvalidOperationException();
    public Guid UserId => Guid.Parse(User.FindFirstValue("sub")!);
    public bool IsAdmin => User.IsInRole("admin");
}
 
builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped<IUserContext, HttpUserContext>();

В тестах подставляется fake; в фоновых задачах — «системный» контекст.

Проброс токена между сервисами

ВариантСмыслКогда
Пробросить входящий Bearer (delegation)вызываем от имени пользователявнутренние вызовы с проверкой прав по пользователю
Token Exchange (RFC 8693)обмен токена на токен для другого audбезопасная делегация
Client Credentialsсервис действует от своего именисистемные операции
builder.Services.AddHttpClient<IBillingClient, BillingClient>()
    .AddHeaderPropagation(o => o.Headers.Add("Authorization"));

Нюансы и подводные камни

  • IHttpContextAccessor недоступен вне запроса (фоновые задачи, очередь): передавайте пользователя явно в сообщении.
  • Не доверяйте заголовкам вида X-User-Id, пришедшим от клиента; только от gateway после проверки токена.
  • Пробрасывайте aud осознанно: токен для API A не должен подходить API B.
  • Логируйте sub, но не токен.

Практика

  1. Внедрите IUserContext в сервис и напишите unit-тест с fake.
  2. Настройте проброс Authorization и X-Correlation-Id между двумя сервисами.
  3. Реализуйте Token Exchange в Keycloak.

Вопросы с ответами

Связанные темы