Ответ
В микросервисах авторизация строится вокруг принципа единого входа (SSO) и передачи контекста пользователя между сервисами, обычно с использованием токенов.
Базовая архитектура:
- Сервис аутентификации (Identity Provider - IdP): Отдельный микросервис (например, на базе IdentityServer, Keycloak или Azure AD), отвечающий за логин, выдачу и валидацию токенов.
- API Gateway: Единая точка входа, которая часто берет на себя первичную проверку токена (валидация подписи, срока жизни).
- Микросервисы: Получают проверенный токен и извлекают из него данные для авторизации (claims).
Детали реализации в .NET-экосистеме:
1. Выбор и валидация токена:
- JWT (JSON Web Token): Самый популярный выбор. Микросервис проверяет подпись токена с помощью публичного ключа от IdP.
- Настройка в микросервисе:
services.AddAuthentication("Bearer") .AddJwtBearer(options => { options.Authority = "https://auth.mycompany.com"; // Адрес IdP options.Audience = "orders-api"; // Имя ресурса (ресурсного сервера) // Валидация происходит автоматически через метаданные с Authority });
2. Передача контекста и авторизация внутри сервиса:
- После аутентификации
HttpContext.Userзаполняется claims из токена. -
Авторизация делается на основе этих claims:
// Проверка роли (если роль передается как claim) [Authorize(Roles = "Admin,Manager")] public IActionResult GetConfidentialData() { ... } // Проверка кастомного claim [Authorize(Policy = "MustBeFromDepartmentX")] public IActionResult GetDepartmentData() { ... }// Регистрация политики в Program.cs services.AddAuthorization(options => { options.AddPolicy("MustBeFromDepartmentX", policy => policy.RequireClaim("department", "X")); });
3. Продвинутые сценарии и проблемы:
- Проблема: Частая валидация токена по сети (introspection) или запрос публичного ключа создает нагрузку на IdP и задержки.
- Решение: Кеширование. Кешировать публичные ключи (JWKS) и результаты интроспекции на уровне микросервиса или API Gateway.
- Проблема: Токену нужны права для вызова других микросервисов (цепочка вызовов).
- Решение:
- Delegation / On-behalf-of flow (OBO): Первый сервис может обменять свой токен на новый, предназначенный для вызова второго сервиса, с теми же или уменьшенными правами.
- Передача исходного токена: Более просто, но менее безопасно — передавать исходный токен дальше по цепочке.
4. Паттерн Sidecar для единообразия: Чтобы не дублировать код аутентификации в каждом сервисе, можно вынести его в sidecar-контейнер (например, Envoy Proxy), который будет прозрачно проверять токены перед передачей запроса основному сервису.
5. Безопасность на уровне сообщений (для событий):
В event-driven архитектуре вместе с сообщением (event) также необходимо передавать контекст безопасности (например, User-Id или Authorization заголовок), чтобы сервис-подписчик мог проверить права на обработку этого события.
Итоговая рекомендация: Начинайте с простой схемы (JWT + валидация в каждом сервисе), а по мере роста вводите кеширование, API Gateway и, возможно, sidecar для централизации инфраструктурных concerns.
Ответ 18+ 🔞
Слушай, ну как объяснить про эту вашу авторизацию в микросервисах... Представь, что у тебя не сервисы, а клуб строгого режима. Чтобы пройти, нужна одна общая карта — это и есть SSO, единый вход, блядь. А дальше уже внутри, от бара к танцполу, ты эту карточку показываешь, и тебя пускают, потому что она нормальная.
Как это всё устроено, по косточкам:
- Вышибала-одиночка (Identity Provider). Это отдельная контора, типа IdentityServer или Keycloak. Только он решает, выдать тебе билет (токен) или послать нахуй. Он один такой главный по лицам.
- Главный фейсконтроль (API Gateway). Ты приходишь в клуб, а тебя на входе шмонают: билет не просрочен, подпись начальства на нём есть? Быстрая проверка, чтобы явных клоунов не пускать.
- Охранники внутри (Микросервисы). Ну зашёл ты в бар. Бармен тоже глянет на твой билет: «Ага, claim «age» >= 18, окей, наливаю». То есть каждый сервис сам решает, что тебе можно, на основе данных в том самом билете.
А теперь как это в .NET делается, без соплей:
1. Билет (JWT) и его проверка. Все эти ваши JWT — это просто бумажка с печатью. Микросервис должен верить, что печать настоящая. Для этого у него есть публичный ключ от того самого вышибалы (IdP).
services.AddAuthentication("Bearer")
.AddJwtBearer(options =>
{
options.Authority = "https://auth.mycompany.com"; // Где сидит наш вышибала
options.Audience = "orders-api"; // Название этого заведения, куда билет
});
Всё, блядь. Фреймворк сам будет ходить за ключами, проверять подпись и срок. Ты даже не вспотеешь.
2. Ну проверили билет, и чо? А потом из этого билета вытаскиваются claims — данные о пользователе. И вот с ними уже можно работать.
// Например, пускать только админов и менеджеров
[Authorize(Roles = "Admin,Manager")]
public IActionResult GetConfidentialData() { ... }
// Или только своих, с отдела X
[Authorize(Policy = "MustBeFromDepartmentX")]
public IActionResult GetDepartmentData() { ... }
А политику эту объявляешь в настройках, вот так:
services.AddAuthorization(options =>
{
options.AddPolicy("MustBeFromDepartmentX", policy =>
policy.RequireClaim("department", "X")); // Проверяем claim 'department'
});
Всё просто, как три копейки. Билет есть с нужным штампом — проходи, нет — иди нахуй.
3. А где подводные ебеня? А они всегда есть, ёпта.
- Проблема: Каждый раз бегать к вышибале (IdP) проверять ключи — долго и того его сервер сломается.
- Решение: Кешировать, блядь! Кешировать эти публичные ключи у себя. Раз получил — на час запомнил и не дергаешь.
- Проблема: Один сервис хочет позвать другой от имени того же пользователя. Билет-то на первый сервис выписан.
- Решение: Есть flow «от имени» (OBO). Первый сервис может сказать: «Вышибала, вот мой билет, дай мне новый, но для вызова вот того второго бара». Или, по-простому, тупо передать исходный токен дальше, но это как-то не очень безопасно, конечно.
4. Умный паттерн для ленивых (Sidecar). А чтобы в каждом микросервисе не копипастить один и тот же код проверки билетов, можно поступить хитро. Вынести этого «охранника» в отдельный контейнер-напарника (типа Envoy), который будет стоять перед основным сервисом и делать всю грязную работу. Сервис получает запрос уже только с готовым, проверенным контекстом пользователя. Красота, ебать.
5. А как в асинхронщине (event-driven)?
А вот тут засада, чувак. Отправил событие в шину, а в нём контекста пользователя нет. Как подписчику проверить, имеет ли он право это событие обрабатывать? Поэтому обязательно нужно в само сообщение засовывать заголовки авторизации или хотя бы User-Id. Чтобы не получилось, что твоё событие про оплату смотрит какой-то левый сервис, которому не положено.
Итог, коротко: Не выёбывайся сразу. Начни с простого — JWT, проверка в каждом сервисе. Когда всё вырастет и начнёт тормозить, добавишь кеширование ключей, потом API Gateway, а там, глядишь, и до sidecar-ов дойдёшь. Главное — чтобы билет был один, а проверяли его все.