IAM. Аутентификация и авторизация
Бизнес-требования
- Обеспечить контроль доступа к функциональности прикладного SaaS-сервиса на основе учетных данных от IAM
- Каждый входящий запрос должен подтверждаться JWT-токеном, подпись которого верифицирована с использованием актуальных публичных ключей от IAM.
- Обеспечить аутентификацию и авторизацию в рамках тенантной модели.
- Фиксация фактов успешного входа, ошибок валидации токена и попыток несанкционированного доступа должна передаваться в централизованную систему аудита платформы в соответствии с утвержденной метамоделью.
- Обеспечить отказоустойчивость механизма проверки токенов: кэширование публичных ключей с настраиваемым временем жизни, автоматическое обновление ключей при истечении срока, логирование ошибок связи с IAM без прерывания обработки валидных запросов.
Решение
Настройка фильтров аутентификации
Системная логика (бэкенд)
План работ:
- Реализовать подключение двух фильтров аутентификации через Maven-зависимости: plat-jwt-authentication-starter для продуктивной среды и plat-stumb-jwt-authentication-starter для локальной разработки, с возможностью переключения через Spring-профиль internal
- Настроить обработку входящих запросов на уровне фильтра Spring Security: перехват заголовка Authorization, извлечение JWT, парсинг токена, проверка подписи с использованием публичного ключа, полученного от IAM по доверенному URL
- Реализовать механизм кэширования публичных ключей в памяти приложения с настраиваемым временем жизни (keysLifetime), автоматическим обновлением при истечении и обработкой ошибок получения ключей
- Обеспечить валидацию стандартных полей JWT: exp (срок действия), iss (соответствие доверенным issuer из конфигурации), aud (опционально), nbf (не раньше)
- Реализовать формирование объекта Authentication Spring Security на основе данных из токена: principal (идентификатор пользователя), credentials (сырой токен), details (маппинг claims токена в HashMap), authorities (извлечение ролей и групп)
- Настроить поддержку мультитенантности: чтение tenant_id из токена или заголовков, передача контекста в последующие обработчики запроса, обеспечение изоляции данных на уровне бизнес-логики
- Реализовать механизм игнорирования фильтра для публичных эндпоинтов через конфигурацию web.ignoring(): /health, /actuator, /swagger-ui, /v3/api-docs и другие технические маршруты
- Обеспечить интеграцию с системой аудита: регистрация событий FAILED_LOGIN при ошибках валидации, AUTHORIZED_SUCCESS при успешной аутентификации, передача метаданных (идентификатор сессии, запрос, результат)
- Реализовать обработку ошибок: возврат корректных HTTP-статусов (401 при отсутствии/невалидности токена, 403 при недостатке прав), логирование инцидентов без утечки чувствительных данных
- Настроить переключение между фильтрами в SecurityConfiguration: проверка активного профиля через Environment, применение jwtSecurityConfigurerAdapter для ПРОМ, stumbJwtSecurityConfigurerAdapter для internal
Хранилище конфигурации аутентификации
Блок auth (для продуктивной среды):
- urls – массив строк, доверенные issuer-адреса для валидации токенов, проверка по префиксу
- authorizeUrl – строка, адрес сервиса получения прав (UFS Provider), может быть пустым при отсутствии проверки @PreAuthorize
- keysLifetime – число, время жизни публичного ключа в кэше (в минутах или секундах)
- sslIgnore – логический тип, пропуск валидации сертификата при обращении к IAM (только для DEV)
- supportMultitenancy – логический тип, обязательно true при работе через platform-gateway
- useHttpIAM – логический тип, замена HTTPS на HTTP при обращении к IAM (для внутренних стендов)
- useIAMPort – число, принудительная установка порта при обращении к IAM
Блок keycloak (дополнительные настройки клиента):
- sslIgnore – дублирующий флаг для совместимости
- useHttpIAM, useIAMPort – аналогично блоку auth
Блок localauth (для локального запуска):
- profilesDir – строка, путь к директории с профилями (абсолютный или относительный)
- defaultProfile – строка, имя профиля по умолчанию (без расширения)
- ufsProviderUrl – строка, адрес для генерации токена в режиме мока
Методы конфигурации
Инициализация фильтра аутентификации:
- При старте приложения чтение конфигурации из application.yml
- Валидация обязательных параметров (urls для ПРОМ, profilesDir для local)
- Инициализация клиента получения ключей с учетом sslIgnore, useHttpIAM, useIAMPort
- Настройка кэша ключей с параметром keysLifetime
- Регистрация фильтра в цепочке Spring Security
Обработка входящего запроса (основной поток):
- Перехват запроса до выполнения бизнес-логики
- Проверка наличия заголовка Authorization
- Извлечение токена после префикса Bearer
- Парсинг JWT без проверки подписи (чтение header, payload)
- Извлечение iss, сравнение с доверенными urls
- Получение публичного ключа: из кэша или запрос к IAM
- Верификация подписи токена
- Проверка exp, nbf, aud (при наличии)
- Извлечение claims: user_id, username, roles, groups, tenant_id, service_name
- Формирование объекта Authentication с маппингом claims в details
- Помещение Authentication в SecurityContextHolder
- Передача управления следующему фильтру или контроллеру
Обработка ошибок валидации:
- При отсутствии токена: возврат 401 Unauthorized, событие в аудит
- При невалидной подписи: возврат 401, логирование инцидента, событие FAILED_LOGIN
- При истечении срока: возврат 401, событие с причиной token_expired
- При несоответствии issuer: возврат 401, логирование подозрительного запроса
- При ошибке связи с IAM: возврат 503 или использование кэшированного ключа (в зависимости от настройки)
Механизм кэширования ключей:
- Хранение пары {kid: publicKey} в in-memory хранилище
- Фиксация времени загрузки ключа
- При запросе ключа: проверка возраста записи, при превышении keysLifetime – принудительное обновление
- При обновлении: запрос к JWKS-эндпоинту IAM, парсинг ответа, замена кэша
- Обработка ошибок обновления: сохранение старого ключа, логирование, повтор через экспоненциальную задержку
Поддержка мультитенантности:
- Извлечение tenant_id из claims токена или заголовка X-Tenant-Id
- Валидация формата идентификатора
- Передача контекста через ThreadLocal или RequestContext
- Обеспечение доступа к tenant_id в бизнес-сервисах и репозиториях
- Фильтрация данных по тенанту на уровне запросов к БД (при необходимости)
Интеграция с аудиторской системой:
- При каждом событии аутентификации формирование DTO с полями: sessionId, userId, timestamp, eventType, result, metadata
- Отправка события в audit-service по настроенному URL (синхронно или асинхронно в зависимости от mode)
- При асинхронном режиме: использование очереди или пула потоков для неблокирующей отправки
- Обработка ошибок отправки: логирование, повторная отправка (опционально)
Пользовательский интерфейс
Модуль не генерирует визуальных компонентов. Его присутствие определяется поведением системы при обработке запросов.
При успешной валидации токена интерфейс отображает запрашиваемые экраны и элементы управления в соответствии с ролевым профилем пользователя, который фронтенд получает после первого успешного ответа от сервера или декодирует из токена локально.
При отсутствии заголовка авторизации, истечении срока действия токена или нарушении целостности подписи интерфейс перехватывает ответ с кодом 401 и инициирует процесс перенаправления на точку входа платформы (например, на Gateway или страницу логина).
При получении ответа с кодом 403 интерфейс отображает стандартизированное сообщение об ограничении доступа без раскрытия деталей политики безопасности.
Навигационные элементы, требующие повышенных привилегий (например, разделы управления пользователями, настройками тенанта), рендерятся условно на основе флагов состояния сессии или декодированных ролей из токена.
В режиме локальной разработки интерфейс может отображать индикатор использования мок-аутентификации (опционально, только для internal-профиля).
Сценарии использования
Пользователь запускает прикладное решение в браузере. Клиентское приложение проверяет наличие токена в локальном хранилище. При наличии токена формируется запрос к API с заголовком Authorization: Bearer <JWT>. Сервис проверяет подпись, срок, issuer. При успехе возвращает запрошенные данные. Интерфейс отображает контент.
Пользователь работает с сервисом, срок действия токена истекает. Следующий запрос возвращает 401. Клиент перехватывает ошибку, очищает локальное хранилище, перенаправляет пользователя на точку входа платформы. После успешного логина платформа возвращает новый токен, клиент сохраняет его и повторяет исходный запрос.
Пользователь с ролью обычного пользователя пытается получить доступ к эндпоинту /api/admin/tenants. Сервис проверяет authorities в объекте Authentication, не находит требуемой роли, возвращает 403. Интерфейс отображает сообщение «Доступ запрещен», кнопка или ссылка на этот раздел скрыта или неактивна.
Разработчик запускает сервис локально с активным профилем internal. Сервис применяет stumbJwtSecurityConfigurerAdapter. При запросе без токена или с тестовым токеном сервис читает профиль из profilesDir/defaultProfile.json, формирует Authentication на основе мок-данных. Разработка и отладка продолжаются без подключения к внешнему IAM.
Администратор тенанта выполняет действия управления. Сервис извлекает роль tenant-admin из токена, разрешает доступ к административным методам. Все действия фиксируются в аудите с привязкой к идентификатору пользователя и тенанта.
Системная логика (фронтенд)
При инициализации приложения выполняется проверка наличия токена в локальном хранилище (localStorage/cookies). При наличии токена он добавляется в заголовок Authorization всех последующих запросов через HTTP-интерцептор.
При получении ответа с кодом 401 интерцептор инициирует очистку токена и перенаправление на точку входа платформы (адрес точки входа конфигурируется или получается из метаданных шлюза).
При получении ответа с кодом 403 интерфейс отображает уведомление с текстом, не раскрывающим детали политики безопасности, и блокирует выполнение действия, инициировавшего запрос.
Для условного рендеринга элементов интерфейса фронтенд декодирует payload JWT локально (без проверки подписи) для извлечения ролей и флагов. На основе этих данных скрываются или отображаются навигационные элементы, кнопки действий, разделы настроек.
При первом успешном ответе от сервера фронтенд может сохранить профиль пользователя (извлеченный из заголовков ответа или отдельного эндпоинта /user/personal) для оптимизации последующих проверок прав без декодирования токена.
В режиме разработки (определяется по переменной окружения) фронтенд может использовать мок-токен или отключать проверку авторизации для упрощения отладки, при условии, что бэкенд настроен на прием таких запросов через профиль internal.
При работе с мультитенантной архитектурой фронтенд передает tenant_id в заголовке запроса (если требуется) или полагается на его извлечение из токена на стороне бэкенда. При переключении между тенантами (если поддерживается) выполняется обновление токена или заголовков с последующим повтором запроса.
No Comments