Контекст пользователя в сервисах (UserContext) и проброс токена между сервисами
↑ 3.4 Аутентификация и авторизация · ← Предыдущая · → Следующая
Зачем это на собесе
Практика микросервисов: как сервис узнаёт «текущего пользователя» и как вызвать другой сервис от его имени.
Объяснение
Не тащите 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, но не токен.
Практика
- Внедрите
IUserContextв сервис и напишите unit-тест с fake. - Настройте проброс
AuthorizationиX-Correlation-Idмежду двумя сервисами. - Реализуйте Token Exchange в Keycloak.
Вопросы с ответами
Как получить текущего пользователя в сервисе?
Через абстракцию над
IHttpContextAccessor/ClaimsPrincipal, зарегистрированную как scoped.
Пробросить токен или использовать service-токен?
Если права зависят от пользователя — пробросить/обменять его токен; для системных задач — Client Credentials.
Как передать пользователя в асинхронной обработке?
Положить идентификатор пользователя в сообщение и восстановить контекст в обработчике.