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

«Как вы реализуете авторизацию в микросервисной архитектуре?» — вопрос из категории Безопасность, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В микросервисах авторизация строится вокруг принципа единого входа (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.