Как вы реализуете авторизацию в микросервисной архитектуре?

Ответ

В микросервисах авторизация строится вокруг принципа единого входа (SSO) и передачи контекста пользователя между сервисами, обычно с использованием токенов.

Базовая архитектура:

  1. Сервис аутентификации (Identity Provider - IdP): Отдельный микросервис (например, на базе IdentityServer, Keycloak или Azure AD), отвечающий за логин, выдачу и валидацию токенов.
  2. API Gateway: Единая точка входа, которая часто берет на себя первичную проверку токена (валидация подписи, срока жизни).
  3. Микросервисы: Получают проверенный токен и извлекают из него данные для авторизации (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, единый вход, блядь. А дальше уже внутри, от бара к танцполу, ты эту карточку показываешь, и тебя пускают, потому что она нормальная.

Как это всё устроено, по косточкам:

  1. Вышибала-одиночка (Identity Provider). Это отдельная контора, типа IdentityServer или Keycloak. Только он решает, выдать тебе билет (токен) или послать нахуй. Он один такой главный по лицам.
  2. Главный фейсконтроль (API Gateway). Ты приходишь в клуб, а тебя на входе шмонают: билет не просрочен, подпись начальства на нём есть? Быстрая проверка, чтобы явных клоунов не пускать.
  3. Охранники внутри (Микросервисы). Ну зашёл ты в бар. Бармен тоже глянет на твой билет: «Ага, 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-ов дойдёшь. Главное — чтобы билет был один, а проверяли его все.