1. Введение: Постановка задачи на проекте

Представьте, что вы — системный аналитик в крупной компании, управляющей интернет-магазином. У вас есть несколько платформ: фронт-система (веб-интерфейс магазина, где клиенты просматривают товары и оформляют заказы), платформа обработки заказов (backend, который управляет заказами и доставкой) и аналитическая платформа для отчётов. Прилетает задача: настроить доступ из фронт-системы магазина к платформе обработки заказов через общекорпоративный Keycloak — единый сервер аутентификации (Identity Provider, IdP). Цель — чтобы пользователи (например, менеджеры магазина) могли выполнять действия (скажем, подтверждать заказы), а JWT-токен, выдаваемый Keycloak, содержал нужные данные, такие как роль order_manager или атрибут отдела.

Это не проектирование системы с нуля, а типичная задача аналитика: разобраться в существующей инфраструктуре, определить, какие роли и атрибуты нужны, и настроить интеграцию. Проблема в том, что без понимания, как работает Keycloak и OIDC, аналитик может "залипнуть". С кем общаться? С разработчиками фронт-системы, чтобы они добавили поддержку токенов? С админами Keycloak, чтобы настроили realm? С владельцами платформы заказов, чтобы уточнить роли? И что именно обсуждать: какие scopes, как маппить роли, как тестировать токены? Непонимание приводит к задержкам, ошибкам в доступах (например, менеджер видит чужие заказы) или даже уязвимостям.

Цель этой статьи — показать, как системному аналитику подойти к такой задаче. Мы разберём принципы Single Sign-On (SSO) и OpenID Connect (OIDC), изучим Keycloak как инструмент для их реализации, и на примере простого веб-приложения (React-frontend, Node.js-backend, Docker) покажем, как настроить доступ. В конце вернёмся к нашей задаче и объясним, как обеспечить, чтобы JWT содержал нужные данные, и что должен обсудить аналитик. Это поможет не только решить задачу, но и избежать типичных ошибок в корпоративных проектах.

Для разработчиков статья даёт практический пример OIDC с использованием PKCE и верификации JWT. Это поможет быстро внедрить SSO без написания сложной логики аутентификации. Для архитекторов статья объясняет, как Keycloak вписывается в микросервисную архитектуру, поддерживает масштабируемость (через PostgreSQL или Active Directory) и обеспечивает безопасность (PKCE, HTTPS).

2. Принципы SSO и OIDC

Single Sign-On (SSO) и OpenID Connect (OIDC) — ключевые технологии для нашей задачи: обеспечить доступ из фронт-системы (reports-frontend) к API отчётов (reports-api) через общекорпоративный Keycloak, чтобы JWT-токен содержал роль report_user. В контексте e-commerce это можно представить как интерфейс интернет-магазина, где менеджеры запрашивают отчёты о продажах, а Keycloak управляет аутентификацией. Для системного аналитика понимание SSO и OIDC критично, чтобы правильно составить заявку на настройку и согласовать с командами (админы Keycloak, разработчики, владельцы API), какие данные должны быть в токене. Без этого аналитик может не знать, что обсуждать (роли, атрибуты) и с кем, что приведёт к ошибкам, например, отсутствию роли в JWT.

Для наглядности начнём с диаграммы, показывающей поток OIDC:

Эта схема показывает: пользователь логинится через Keycloak, фронт-система получает JWT с ролью report_user, а API отчётов проверяет токен, чтобы вернуть данные отчёта.
Эта схема показывает: пользователь логинится через Keycloak, фронт-система получает JWT с ролью report_user, а API отчётов проверяет токен, чтобы вернуть данные отчёта.

Что такое SSO

SSO (Single Sign-On) позволяет пользователю войти один раз и получить доступ ко всем подключённым системам без повторного ввода логина и пароля. В нашей задаче это значит, что менеджер входит в reports-frontend и сразу может запросить отчёты через reports-api. SSO:

  • Упрощает опыт: Один логин для всех систем.

  • Повышает безопасность: Централизованное управление через Keycloak.

  • Снижает нагрузку: Меньше запросов в IT-поддержку.

SSO работает через Identity Provider (IdP), такой как Keycloak, который аутентифицирует пользователя и выдаёт токены. Аналитик должен понять, какие роли (например, report_user) нужны в токене для доступа к API.

OpenID Connect (OIDC) и его роль

OIDC — протокол аутентификации, построенный поверх OAuth 2.0, решающий две задачи:

  • Аутентификация: Подтверждает, кто пользователь (например, access1).

  • Авторизация: Определяет, что он может делать (например, доступ к отчётам через report_user).

OIDC использует Authorization Code Flow:

  1. Пользователь заходит в reports-frontend и кликает "Войти".

  2. Фронт-система перенаправляет на Keycloak для логина.

  3. Keycloak выдаёт authorization code, который обменивается на JWT-токены (ID Token для идентификации, Access Token для доступа).

  4. reports-frontend отправляет Access Token на /reports, где API проверяет роль report_user.

Аналитик должен согласовать, чтобы JWT содержал report_user, и обсудить это с админами Keycloak и разработчиками API.

Структура JWT

JWT (JSON Web Token) — компактный токен в формате JSON, выдаваемый Keycloak. Он состоит из трёх частей: Header, Payload, Signature (разделены точками, закодированы в base64). Payload важен для аналитика, так как содержит:

  • sub: ID пользователя (например, access1).

  • nameemail: Данные пользователя (например, access1@example.com).

  • realm_access.roles: Роли на уровне realm (например, ["report_user"]).

  • Кастомные атрибуты: Если нужны (например, department), их добавляют через мапперы в Keycloak.

Аналитик должен уточнить у владельцев API, какие роли (например, report_user) нужны в токене, и передать это в заявке.

Сравнение с другими протоколами

OIDC — лучший выбор для веб-приложений, таких как reports-frontend:

  • SAML: Использует XML, сложнее в настройке, для старых систем.

  • OAuth 2.0: Только авторизация, без аутентификации.

  • Kerberos: Для локальных сетей, не подходит для веба.

OIDC прост в интеграции благодаря JSON и поддержке Keycloak.

Риски и задачи аналитика

OIDC уязвим без правильной настройки:

  • CSRF-атаки: Злоумышленник подделывает запрос. Защищает параметр state (случайная строка, проверяемая клиентом).

  • Перехват токенов: Требуется HTTPS.

  • Ошибки в токене: Если report_user отсутствует, доступ не сработает.

Аналитик должен обсудить:

  • Включение state для защиты.

  • Роли в JWT (например, report_user).

  • Тестирование токенов (например, декодирование на jwt.io).

Без понимания OIDC аналитик может не знать, что включить в заявку или с кем говорить, что приведёт к задержкам.

3. Keycloak как инструмент для реализации OIDC

Keycloak — это мощный open-source инструмент для управления идентификацией и доступом (Identity and Access Management, IAM), который идеально подходит для реализации Single Sign-On (SSO) через протокол OpenID Connect (OIDC). В нашей задаче Keycloak выступает как общекорпоративный Identity Provider (IdP), обеспечивая доступ из фронт-системы интернет-магазина (например, веб-интерфейса для менеджеров) к платформе обработки заказов. Keycloak берёт на себя аутентификацию пользователей и выдачу JWT-токенов с ролями (например, order_manager) и атрибутами (например, department: sales). Для системного аналитика это означает необходимость согласовать настройки Keycloak с разработчиками, администраторами и владельцами платформы, чтобы токен содержал нужные данные, а доступ работал корректно.

Для наглядности начнём с диаграммы, показывающей, как Keycloak связывает фронт-систему и платформу заказов:

Эта схема показывает: Keycloak управляет realm (store-realm), где хранятся пользователи и роли. Фронт-система (store-frontend) получает JWT после логина, а платформа заказов (orders-api) проверяет токен, чтобы разрешить доступ.
Эта схема показывает: Keycloak управляет realm (store-realm), где хранятся пользователи и роли. Фронт-система (store-frontend) получает JWT после логина, а платформа заказов (orders-api) проверяет токен, чтобы разрешить доступ.

Преимущества Keycloak

Keycloak упрощает задачу аналитика в корпоративных проектах, таких как наш интернет-магазин:

  • Интеграция: Поддерживает OIDC для веб-приложений, обеспечивая JSON-токены (JWT) с ролями и атрибутами.

  • Гибкость: Позволяет настроить SSO между фронт-системой и платформой заказов, а также интегрироваться с Active Directory или Google.

  • Безопасность: Встроенные механизмы, такие как хэширование паролей, поддержка MFA и защита от атак (например, через параметр state).

  • Интерфейс: Веб-админка позволяет аналитику настраивать пользователей, роли и клиентов без программирования.

  • Open-source: Бесплатен, с активным сообществом и возможностью кастомизации.

Аналитик должен обсудить с разработчиками фронт-системы, как она будет запрашивать токены, с админами Keycloak — как настроить роли в realm, и с владельцами платформы заказов — какие данные нужны в JWT.

Основные компоненты Keycloak

Keycloak состоит из нескольких элементов, которые аналитик должен понимать, чтобы координировать настройки:

  • Realms: Изолированные "домены" для пользователей, ролей и клиентов. Например, store-realm может содержать пользователей магазина и их роли.

  • Clients: Приложения, подключающиеся к Keycloak. В нашей задаче это store-frontend (фронт-система) и orders-api (платформа заказов). Clients определяют redirect URI и scopes (например, какие роли включать в JWT).

  • Users и Roles: Пользователи (например, access1) и их роли (например, order_manager). Роли бывают на уровне realm или клиента.

  • Flows: Настраиваемые сценарии логина, например, требование пароля или добавление MFA.

Эти компоненты работают вместе: realm изолирует настройки, client запрашивает токены, а роли определяют доступ. Аналитик должен обеспечить, чтобы order_manager попала в JWT для платформы заказов.

Установка и базовая настройка

Для тестирования или разработки Keycloak разворачивается через Docker с PostgreSQL для хранения данных. Это не задача аналитика, но он должен понимать процесс, чтобы обсудить его с DevOps или админами. Вот пример docker-compose.yml:

services:
  keycloak_db:
    image: postgres:14
    environment:
      POSTGRES_DB: keycloak_db
      POSTGRES_USER: keycloak_user
      POSTGRES_PASSWORD: keycloak_password
    volumes:
      - ./postgres-keycloak-data:/var/lib/postgresql/data
    ports:
      - "5433:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U keycloak_user -d keycloak_db"]
      interval: 5s
      timeout: 5s
      retries: 5

  keycloak:
    image: quay.io/keycloak/keycloak:21.1
    environment:
      KEYCLOAK_ADMIN: admin
      KEYCLOAK_ADMIN_PASSWORD: admin
      KC_DB: postgres
      KC_DB_URL: jdbc:postgresql://keycloak_db:5432/keycloak_db
      KC_DB_USERNAME: keycloak_user
      KC_DB_PASSWORD: keycloak_password
    command: 
      - start-dev
    ports:
      - "8080:8080"
    depends_on:
      keycloak_db:
        condition: service_healthy

Разверните Keycloak командой:

docker compose up -d
  • -d запускает в фоновом режиме. Если порт 8080 занят, проверьте с помощью netstat -tuln (Linux) или netstat -aon (Windows) и замените, например, на 8081:8080.

  • Keycloak доступен по http://localhost:8080, логин/пароль: admin/admin.

Для настройки аналитик может:

  1. В админ-панели выбрать "Add realm" и создать store-realm.

  2. Настроить пользователей и роли (например, access1 с ролью order_manager).

  3. Создать clients (store-frontendorders-api) с нужными redirect URI.

Форма создания нового realm в админ-панели Keycloak
Форма создания нового realm в админ-панели Keycloak
Форма создания нового realm в админ-панели Keycloakstore-realm
Форма создания нового realm в админ-панели Keycloakstore-realm

Аналитик должен обсудить с админами Keycloak, чтобы роли и атрибуты (например, department: sales) были добавлены в JWT, и с разработчиками — чтобы фронт-система корректно запрашивала токены.

4. Погружение в realms Keycloak

Realms (домены) в Keycloak — это изолированные пространства для управления пользователями, ролями и клиентами. Они позволяют разделять настройки для разных систем, таких как фронт-система интернет-магазина и платформа обработки заказов в нашей задаче. Каждый realm содержит своих пользователей (например, access1), роли (например, order_manager) и клиентов (например, store-frontendorders-api), обеспечивая безопасность и порядок. Для системного аналитика realms — ключ к настройке доступа: он должен определить, какие роли и атрибуты попадут в JWT-токен, и согласовать это с администраторами Keycloak и разработчиками платформ.

Для наглядности начнём с диаграммы, показывающей, как realms изолируют данные и связывают платформы:

Эта схема показывает: Keycloak управляет двумя realms (store-realm для магазина, orders-realm для заказов). Пользователь access1 из store-realm получает JWT с ролью order_manager, который платформа заказов проверяет через клиент orders-api. Федерация между realms позволяет настроить SSO.
Эта схема показывает: Keycloak управляет двумя realms (store-realm для магазина, orders-realm для заказов). Пользователь access1 из store-realm получает JWT с ролью order_manager, который платформа заказов проверяет через клиент orders-api. Федерация между realms позволяет настроить SSO.

Что такое realms и зачем они нужны

Realm — это "контейнер" в Keycloak, который изолирует данные и настройки. Без realms все пользователи и клиенты смешивались бы, что привело бы к хаосу и рискам (например, менеджер магазина мог бы случайно получить доступ к аналитике). В нашей задаче realms решают следующие проблемы:

  • Изоляция: Пользователи и роли store-realm не пересекаются с orders-realm, что безопасно.

  • Гибкость: Каждый realm может иметь свои настройки логина (например, MFA для платформы заказов).

  • Федерация: Realms могут связываться для SSO, позволяя менеджеру из store-realm работать с заказами в orders-realm.

  • Управление: Аналитик может настроить роли (например, order_manager) в одном realm и маппить их в другом.

Для аналитика realms — инструмент для структурирования доступа. Он должен обсудить с владельцами платформы, какие роли нужны (например, edit_orders для подтверждения заказов), и с админами Keycloak — как их настроить.

Настройка realms

Realms настраиваются двумя способами: вручную через админ-панель Keycloak или автоматически через импорт JSON-файла. Аналитик чаще работает с админ-панелью, так как это не требует кодирования.

Ручная настройка

  1. Зайдите в админ-панель Keycloak (http://localhost:8080, логин/пароль: admin/admin).

  2. Нажмите "Add realm" (или "Create Realm") в левом меню.

  3. Укажите имя (например, store-realm) и включите (Enabled: true).

    В новом realm настройте:

    • Users: В разделе "Users" → "Add user" создайте пользователя (например, access1, email: access1@example.com). В "Credentials" задайте пароль (accesspass1, temporary: false).

    • Roles: В "Realm Roles" → "Create role" добавьте роль order_manager с описанием "Manager for order operations".

    • Clients: В "Clients" → "Create client" создайте клиент store-frontend (publicClient: true, redirectUris: http://localhost:3000/*) и orders-api (bearerOnly: true).

    • Role Mappings: В "Users" → access1 → "Role Mappings" присвойте роль order_manager.

Настройка через импорт JSON

Для автоматизации (например, в CI/CD) аналитик может подготовить JSON-файл с конфигурацией realm и передать его админам Keycloak. Пример для store-realm:

{
  "realm": "store-realm",
  "enabled": true,
  "roles": {
    "realm": [
      {
        "name": "order_manager",
        "description": "Manager for order operations"
      }
    ]
  },
  "users": [
    {
      "username": "access1",
      "enabled": true,
      "email": "access1@example.com",
      "credentials": [
        {
          "type": "password",
          "value": "accesspass1",
          "temporary": false
        }
      ],
      "realmRoles": ["order_manager"]
    }
  ],
  "clients": [
    {
      "clientId": "store-frontend",
      "enabled": true,
      "publicClient": true,
      "redirectUris": ["http://localhost:3000/*"]
    },
    {
      "clientId": "orders-api",
      "enabled": true,
      "clientAuthenticatorType": "client-secret",
      "secret": "oNwoLQdvJAvRcL89SydqCWCe5ry1jMgq",
      "bearerOnly": true
    }
  ]
}

Импортируйте файл: в админ-панели выберите "Import" → загрузите JSON → подтвердите.

Раздел "Import" в админ-панели Keycloak
Раздел "Import" в админ-панели Keycloak

Передача логинов и паролей в реальной жизни

В production-средах логины и пароли обрабатываются безопасно:

  • Хранение: Пароли хэшируются (PBKDF2 или Argon2). В JSON-файле они в plain-text только для импорта; в базе данных — зашифрованы.

  • Передача: Логины/пароли передаются по HTTPS. Аналитик должен убедиться, что Keycloak настроен на SSL (в production это обязательно).

  • Регистрация: Пользователи могут регистрироваться через интерфейс Keycloak (с email-верификацией) или импортироваться из Active Directory (подробно в следующем разделе). Например, менеджеры магазина синхронизируются из AD, а их роли маппятся в store-realm.

  • Безопасность: Аналитик должен согласовать политики паролей (минимальная длина, expiration) и включение MFA для платформы заказов. Пароли хранятся в secrets (например, KEYCLOAK_ADMIN_PASSWORD в Docker) или HashiCorp Vault.

Для нашей задачи аналитик должен обеспечить, чтобы пользователь access1 получил роль order_manager в store-realm, а JWT-токен содержал эту роль для доступа к orders-api.

Схема настройки ролей в realm

Вот диаграмма, показывающая, как аналитик настраивает роли в realm:

Эта схема показывает процесс: аналитик настраивает realm, пользователей и роли, а затем координирует с разработчиками, чтобы JWT содержал нужные данные.
Эта схема показывает процесс: аналитик настраивает realm, пользователей и роли, а затем координирует с разработчиками, чтобы JWT содержал нужные данные.

5. Хранение пользовательской информации в Keycloak

Keycloak хранит данные пользователей (логины, хэшированные пароли, роли, email) в рамках realms, чтобы обеспечить аутентификацию и авторизацию. В нашей задаче — доступ из фронт-системы (reports-frontend) к API отчётов (reports-api) с JWT, содержащим роль report_user — важно понять, где хранятся пользователи (например, access1) и как их данные (роль report_user) попадают в токен. В контексте e-commerce это можно представить как менеджеров магазина, чьи данные хранятся централизованно. Keycloak поддерживает два основных подхода: локальное хранение в PostgreSQL (типичный выбор для многих проектов) и интеграцию с Active Directory (AD) или LDAP для корпоративных систем. Аналитик должен указать в заявке, откуда брать пользователей и какие данные включать в JWT, координируя с админами Keycloak, разработчиками и владельцами API.

Для наглядности начнём с общей схемы, показывающей, как данные пользователей связаны с хранилищами:

Эта схема показывает: Keycloak хранит данные в reports-realm, используя PostgreSQL или AD. Пользователь access1 получает JWT с ролью report_user, которая проверяется reports-api.
Эта схема показывает: Keycloak хранит данные в reports-realm, используя PostgreSQL или AD. Пользователь access1 получает JWT с ролью report_user, которая проверяется reports-api.

1. Локальное хранение в PostgreSQL

Keycloak часто использует PostgreSQL для хранения данных пользователей, ролей и клиентов. Данные хранятся в таблицах:

  • Users: Логины, email (например, access1access1@example.com).

  • Credentials: Хэшированные пароли (с PBKDF2 или Argon2 для безопасности).

  • Roles: Роли на уровне realm (например, report_user).

Почему это важно: PostgreSQL — стандартный выбор для прототипов или небольших проектов благодаря простоте настройки. Пользователи и роли (например, access1 с report_user) сохраняются в базе и используются для создания JWT.

Пример настройки: В Keycloak задаётся подключение к PostgreSQL через параметры вроде KC_DB_URL. Конкретная реализация с Docker будет показана в части 8.

Преимущества: Простота, всё в одном месте. Недостатки: Не масштабируется для тысяч пользователей, единая точка отказа.

Схема хранения в PostgreSQL:

2. Распределённое хранение

Для крупных систем с тысячами пользователей Keycloak поддерживает кластеризацию с PostgreSQL или другими базами (MySQL, Oracle) через Infinispan (встроенный кэш Keycloak):

  • Infinispan: Синхронизирует данные между нодами Keycloak.

  • Настройка: Требует нескольких нод Keycloak и включения кэша (например, KC_CACHE=ispn).

Почему это важно: Обеспечивает масштабируемость для множества пользователей (например, менеджеров магазина).

Преимущества: Высокая доступность. Недостатки: Сложная настройка, требует инфраструктуры (например, Kubernetes).

Схема кластеризации:

3. Интеграция с Active Directory или LDAP

В корпоративных системах, как наш e-commerce пример, пользователи часто хранятся в Active Directory (AD) или LDAP. Keycloak синхронизирует их через User Federation:

  • Как работает: Keycloak подключается к AD, импортируя пользователей и группы. Группа AD ReportUsers маппится на роль report_user в reports-realm.

  • Настройка:

    1. В админ-панели: "User Federation" → "Add provider" → "ldap".

    2. Укажите URL (ldap://ad.example.com), bind DN, фильтры ((objectClass=user)).

    3. Маппинг: группа ReportUsers → роль report_user.

Почему это важно: Позволяет использовать корпоративные учётные записи, как для менеджеров магазина.

Преимущества: Централизованное управление. Недостатки: Требуется доступ к AD, сложнее настройка.

Схема синхронизации с AD:

4. Передача логинов и паролей

В продакшене данные обрабатываются безопасно:

  • Хранение: Пароли хэшируются (PBKDF2/Argon2). В JSON-импорте (как в части 8) они в plain-text только для настройки.

  • Передача: Логины/пароли передаются по HTTPS. Keycloak требует SSL в продакшене.

  • Регистрация: Пользователи регистрируются через интерфейс Keycloak (с email-верификацией) или синхронизируются из AD.

  • Безопасность: Аналитик указывает в заявке политики паролей (длина, expiration) и MFA. Пароли хранятся в secrets (например, в переменных окружения).

Схема потока логина:

6. PKCE для защиты от атак в OIDC

PKCE (Proof Key for Code Exchange) — это расширение протокола OpenID Connect (OIDC), которое повышает безопасность для public clients, таких как фронт-система интернет-магазина (store-frontend) в нашей задаче. Public clients — это приложения, работающие в браузере (например, React-приложение), где нельзя безопасно хранить секрет клиента. PKCE предотвращает атаки, связанные с перехватом authorization code, когда злоумышленник может украсть код и обменять его на JWT-токен, получив доступ к платформе заказов (orders-api). Для системного аналитика PKCE важно, так как он должен согласовать с разработчиками фронт-системы включение этой защиты и убедиться, что токены (содержащие, например, роль order_manager) выдаются безопасно.

Проблема без PKCE: уязвимость в Authorization Code Flow

В стандартном Authorization Code Flow OIDC фронт-система перенаправляет пользователя на Keycloak для логина, после чего получает authorization code через redirect URI (например, http://localhost:3000/*). Этот код обменивается на JWT-токены (ID Token и Access Token) через Token Endpoint. Проблема в том, что код передаётся через браузер и может быть перехвачен злоумышленником, например:

  • Подмена redirect URI: Злоумышленник перенаправляет код на свой сервер.

  • Вредоносное приложение: Другое приложение с тем же redirect URI использует украденный код.

Без защиты Keycloak не может проверить, что код запрошен именно легитимным клиентом (store-frontend), и злоумышленник может получить токен с ролью order_manager.

Вот диаграмма, показывающая атаку без PKCE:

Эта схема показывает, как злоумышленник перехватывает код и получает токен, обходя фронт-систему.
Эта схема показывает, как злоумышленник перехватывает код и получает токен, обходя фронт-систему.

Решение с PKCE: защищённый поток

PKCE защищает от таких атак, добавляя одноразовый секрет, который подтверждает, что запрос токенов исходит от легитимного клиента. Вот как это работает:

  • Фронт-система генерирует случайную строку — code_verifier (секрет, например, E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM).

  • Из code_verifier создаётся code_challenge — его SHA-256 хеш, закодированный в base64-URL (например, dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk).

  • code_challenge отправляется в запросе на логин, а code_verifier — при обмене кода на токены. Keycloak проверяет, что хеш verifier совпадает с challenge.

Если злоумышленник перехватит код, он не сможет обменять его на токены без code_verifier, известного только фронт-системе.

Вот диаграмма для защищённого потока с PKCE:

Эта схема показывает, как PKCE защищает фронт-систему, делая перехват кода бесполезным.
Эта схема показывает, как PKCE защищает фронт-систему, делая перехват кода бесполезным.

Как работает PKCE в деталях

PKCE — стандарт, рекомендованный RFC 7636, и обязателен для public clients, таких как наш store-frontend. Ключевые шаги:

  1. Генерация: Фронт-система создаёт code_verifier — случайную строку (43–128 символов, base64-URL без =+/). Затем вычисляет code_challenge — SHA-256 хеш verifier, тоже в base64-URL.

  2. Запрос на логин: В Authorization Endpoint отправляется code_challenge и code_challenge_method=S256 (рекомендуемый метод; plain менее безопасен).

  3. Обмен на токены: Фронт-система отправляет code_verifier вместе с кодом. Keycloak хэширует verifier и сравнивает с challenge.

  4. Настройка в Keycloak: Аналитик должен попросить админов включить PKCE для клиента store-frontend в админ-панели (раздел "Clients" → store-frontend → "Advanced Settings" → "Proof Key for Code Exchange Policy" = Enforced). Разработчики фронт-системы добавляют поддержку PKCE в коде (например, в React с @react-keycloak/web).

Аналитик должен:

  • Согласовать с разработчиками фронт-системы включение PKCE.

  • Убедиться с админами Keycloak, что клиент настроен на S256.

  • Проверить, что JWT содержит нужные роли (например, order_manager) после логина.

Роль аналитика

Аналитик координирует безопасность фронт-системы:

  • Обсуждение с разработчиками: Убедиться, что фронт-система отправляет code_challenge и обрабатывает code_verifier.

  • Обсуждение с админами Keycloak: Настройка PKCE для клиента store-frontend.

  • Тестирование: Проверить, что токены выдаются только легитимным клиентам, декодируя JWT (например, на jwt.io).

Без PKCE фронт-система уязвима, что может привести к несанкционированному доступу к платформе заказов. PKCE — обязательная мера для безопасного SSO в нашей задаче.

7. Схема простого веб-приложения и роль Keycloak в нём

Keycloak в нашей задаче выступает как центральный сервер аутентификации (Identity Provider, IdP), связывающий фронт-систему интернет-магазина (store-frontend) с платформой обработки заказов (orders-api). Для системного аналитика важно понять, как эти компоненты взаимодействуют через Keycloak, чтобы обеспечить доступ (например, для роли order_manager) и правильное содержимое JWT-токена (роли, атрибуты). В этой части мы разберём архитектуру типичного веб-приложения, использующего Keycloak, и покажем, как аналитик координирует настройку, чтобы токен содержал нужные данные.

Для наглядности начнём с диаграммы, показывающей, как Keycloak интегрируется с приложением:

Эта схема показывает: пользователь логинится через фронт-систему, Keycloak выдаёт JWT с ролью order_manager, а платформа заказов проверяет токен, чтобы разрешить действия (например, подтверждение заказов). PostgreSQL хранит данные пользователей и роли.
Эта схема показывает: пользователь логинится через фронт-систему, Keycloak выдаёт JWT с ролью order_manager, а платформа заказов проверяет токен, чтобы разрешить действия (например, подтверждение заказов). PostgreSQL хранит данные пользователей и роли.

Архитектура веб-приложения

Типичное веб-приложение состоит из трёх основных компонентов:

  • Фронт-система (Frontend): Веб-интерфейс, где пользователи (например, менеджеры магазина) взаимодействуют с системой. В нашем случае — React-приложение (store-frontend), которое перенаправляет на Keycloak для логина.

  • Платформа заказов (Backend): Сервер, обрабатывающий запросы, например, подтверждение заказов. В нашем случае — Node.js-приложение (orders-api), которое верифицирует JWT-токены.

  • Keycloak: IdP, управляющий аутентификацией и выдающий токены с ролями и атрибутами.

Аналитик должен:

  • Согласовать с разработчиками фронт-системы, как она будет запрашивать токены (например, через OIDC Authorization Code Flow).

  • Убедиться с админами Keycloak, что роли (например, order_manager) и атрибуты (например, department: sales) добавлены в JWT.

  • Попросить разработчиков платформы заказов настроить верификацию токенов.

Роль Keycloak в приложении

Keycloak выступает как "посредник" между фронт-системой и платформой заказов:

  1. Аутентификация: Пользователь заходит в фронт-систему и перенаправляется на Keycloak для логина (например, вводит access1/accesspass1).

  2. Выдача токенов: Keycloak возвращает JWT (ID Token для идентификации, Access Token с ролями, например, order_manager).

  3. Верификация: Фронт-система отправляет Access Token на платформу заказов, которая проверяет его (например, через JWKS — JSON Web Key Set).

  4. Доступ: Если токен содержит нужную роль, платформа разрешает действия (например, подтверждение заказа).

Аналитик должен обсудить с командами:

  • Какие роли и атрибуты включать в JWT (например, order_managerdepartment).

  • Как настроить clients в Keycloak (store-frontendorders-api).

  • Как тестировать токены (например, декодировать на jwt.io).

Поток взаимодействия

Вот подробный поток, показывающий, как Keycloak связывает компоненты:

Эта схема показывает: Keycloak аутентифицирует пользователя, выдаёт JWT, а платформа заказов проверяет токен, чтобы разрешить доступ. Аналитик должен убедиться, что JWT содержит нужные данные.
Эта схема показывает: Keycloak аутентифицирует пользователя, выдаёт JWT, а платформа заказов проверяет токен, чтобы разрешить доступ. Аналитик должен убедиться, что JWT содержит нужные данные.

Роль аналитика

Аналитик координирует настройку:

  • С разработчиками фронт-системы: Убедиться, что клиент (store-frontend) использует OIDC и корректные redirect URI.

  • С админами Keycloak: Настроить realm (store-realm) с пользователями (access1) и ролями (order_manager).

  • С разработчиками платформы заказов: Проверить, что backend верифицирует JWT и извлекает роли.

  • Тестирование: Декодировать JWT (например, на jwt.io) и убедиться, что он содержит order_manager и department: sales.

Связь с задачей

В нашей задаче Keycloak объединяет фронт-систему и платформу заказов через SSO. Аналитик должен:

  • Согласовать, какие роли и атрибуты нужны в JWT для доступа к заказам.

  • Проверить настройки clients (store-frontendorders-api) в Keycloak.

  • Убедиться, что интеграция безопасна (например, использует HTTPS).

Эта архитектура масштабируема: можно добавить новые платформы (например, аналитику) в тот же realm или создать новый, сохранив SSO.

8. Реализация проекта: практический пример

Эта часть показывает, как работает веб-приложение, реализующее доступ из фронт-системы (reports-frontend) к API отчётов (reports-api) через Keycloak, используя протокол OpenID Connect (OIDC). Мы представляем это в контексте e-commerce, где фронт-система может быть интерфейсом интернет-магазина, а API отчётов — платформой для обработки данных. Пример включает React-frontend, Node.js-backend и Keycloak, запущенные через Docker Compose. Мы продемонстрируем, как код настраивает Keycloak, интегрирует фронт- и бэкенд-приложения, и как JWT-токен с ролью report_user обеспечивает доступ к отчётам. Это поможет понять, как технически реализуется Single Sign-On (SSO). Полный код доступен в репозитории https://github.com/vasilokb/keycloak, где вы можете клонировать проект и попробовать его локально.

Для наглядности начнём с диаграммы, показывающей поток запросов:

Эта схема показывает: пользователь логинится через Keycloak, фронт-система получает JWT с ролью report_user, а API отчётов проверяет токен и возвращает данные (например, отчёт { id: 123, title: "Usage Report 45" }).
Эта схема показывает: пользователь логинится через Keycloak, фронт-система получает JWT с ролью report_user, а API отчётов проверяет токен и возвращает данные (например, отчёт { id: 123, title: "Usage Report 45" }).

Структура проекта

Проект организован как микросервисная архитектура, где каждый компонент (фронт-система, бэкенд, Keycloak, база данных) изолирован и запускается в отдельном Docker-контейнере. Это обеспечивает:

  • Модульность: Компоненты независимы, что упрощает разработку и тестирование.

  • Масштабируемость: Сервисы можно масштабировать отдельно (например, добавить ноды Keycloak).

  • Оркестрацию: Docker Compose объединяет компоненты в сеть для взаимодействия.

  • Хранение данных: PostgreSQL сохраняет пользователей и роли Keycloak.

Структура проекта:

.
├── .gitignore
├── docker-compose.yml
├── README.md
├── backend
│   ├── Dockerfile
│   ├── package.json
│   ├── server.js
├── frontend
│   ├── .env
│   ├── Dockerfile
│   ├── nginx.conf
│   ├── package.json
│   ├── public
│   │   ├── index.html
│   ├── src
│   │   ├── App.tsx
│   │   ├── index.tsx
│   │   ├── components
│   │   │   ├── ReportPage.tsx
├── keycloak
│   ├── realm-export.json
├── postgres-keycloak-data

Пояснения к структуре:

  • .gitignore: Исключает временные файлы (например, node_modules) из репозитория.

  • docker-compose.yml: Определяет сервисы (Keycloak, PostgreSQL, frontend, backend) и сеть sprint-8-network.

  • README.md: Документация с описанием запуска и настройки.

  • backend: Node.js-приложение (reports-api):

    • Dockerfile: Инструкции для сборки Docker-образа.

    • package.json: Зависимости (express, jwks-rsa для верификации JWT).

    • server.js: Обрабатывает запросы /reports.

  • frontend: React-приложение (reports-frontend):

    • .env: Переменные окружения (например, REACT_APP_KEYCLOAK_URL).

    • Dockerfile: Сборка React-приложения.

    • nginx.conf: Конфигурация веб-сервера для фронт-системы.

    • package.json: Зависимости (react, @react-keycloak/web).

    • public/index.html: Главная HTML-страница.

    • src: Код фронт-системы, включая App.tsx (логика OIDC) и ReportPage.tsx (интерфейс отчётов).

  • keycloak: Конфигурация Keycloak:

    • realm-export.json: Настройки realm, пользователей, ролей, клиентов.

  • postgres-keycloak-data: Volume для хранения данных PostgreSQL.

Настройка Keycloak

Keycloak настроен через JSON-импорт, создающий realm reports-realm, пользователя access1, роль report_user и клиентов reports-frontendreports-api.

Импорт realm

Файл realm-export.json:

{
  "realm": "reports-realm",
  "enabled": true,
  "roles": {
    "realm": [
      {
        "name": "user",
        "description": "Regular user role"
      },
      {
        "name": "administrator",
        "description": "Administrator role"
      },
      {
        "name": "report_user",
        "description": "Prothetic user role with report access"
      }
    ]
  },
  "users": [
    {
      "username": "regular1",
      "enabled": true,
      "email": "regular1@example.com",
      "firstName": "Regular",
      "lastName": "One",
      "credentials": [
        {
          "type": "password",
          "value": "userpass1",
          "temporary": false
        }
      ],
      "realmRoles": ["user"]
    },
    {
      "username": "superadmin1",
      "enabled": true,
      "email": "superadmin1@example.com",
      "firstName": "Super",
      "lastName": "Admin",
      "credentials": [
        {
          "type": "password",
          "value": "adminpass1",
          "temporary": false
        }
      ],
      "realmRoles": ["administrator"]
    },
    {
      "username": "access1",
      "enabled": true,
      "email": "access1@example.com",
      "firstName": "Access",
      "lastName": "One",
      "credentials": [
        {
          "type": "password",
          "value": "accesspass1",
          "temporary": false
        }
      ],
      "realmRoles": ["report_user"]
    }
  ],
  "clients": [
    {
      "clientId": "reports-frontend",
      "enabled": true,
      "publicClient": true,
      "redirectUris": ["http://localhost:3000/*"],
      "webOrigins": ["http://localhost:3000"],
      "directAccessGrantsEnabled": true
    },
    {
      "clientId": "reports-api",
      "enabled": true,
      "clientAuthenticatorType": "client-secret",
      "secret": "oNwoLQdvJAvRcL89SydqCWCe5ry1jMgq",
      "bearerOnly": true
    }
  ]
}

Пояснения к коду:

  • "realm": "reports-realm": Создаёт изолированный домен для приложения, где хранятся пользователи, роли и клиенты.

  • "roles": Определяет роли, включая report_user, дающую доступ к отчётам.

  • "users": Создаёт пользователя access1 с паролем accesspass1 и ролью report_user, которая включается в JWT.

  • "clients": Настраивает reports-frontend (public client для React, с redirect URI для OIDC) и reports-api (bearer-only для backend, с секретом для верификации).

Docker Compose

Файл docker-compose.yml запускает сервисы:

services:
  keycloak_db:
    image: postgres:14
    environment:
      POSTGRES_DB: keycloak_db
      POSTGRES_USER: keycloak_user
      POSTGRES_PASSWORD: keycloak_password
    volumes:
      - ./postgres-keycloak-data:/var/lib/postgresql/data
    ports:
      - "5433:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U keycloak_user -d keycloak_db"]
      interval: 5s
      timeout: 5s
      retries: 5

  keycloak:
    image: quay.io/keycloak/keycloak:21.1
    environment:
      KEYCLOAK_ADMIN: admin
      KEYCLOAK_ADMIN_PASSWORD: admin
      KC_DB: postgres
      KC_DB_URL: jdbc:postgresql://keycloak_db:5432/keycloak_db
      KC_DB_USERNAME: keycloak_user
      KC_DB_PASSWORD: keycloak_password
    command: 
      - start-dev
      - --import-realm
    volumes:
      - ./keycloak/realm-export.json:/opt/keycloak/data/import/realm-export.json
    ports:
      - "8080:8080"
    depends_on:
      keycloak_db:
        condition: service_healthy

  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    environment:
      REACT_APP_API_URL: http://localhost:8000
      REACT_APP_KEYCLOAK_URL: http://localhost:8080
      REACT_APP_KEYCLOAK_REALM: reports-realm
      REACT_APP_KEYCLOAK_CLIENT_ID: reports-frontend
    depends_on:
      - backend
      - keycloak

  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    ports:
      - "8000:8000"
    depends_on:
      - keycloak

networks:
  default:
    name: sprint-8-network

Пояснения к коду:

  • keycloak_db: PostgreSQL для хранения данных пользователей, ролей и клиентов (keycloak_db, логин: keycloak_user, пароль: keycloak_password).

  • keycloak: Keycloak-сервер подключается к PostgreSQL, импортирует realm-export.json (--import-realm), доступен на http://localhost:8080.

  • frontend: React-приложение на порту 3000, использует переменные окружения для связи с Keycloak (reports-realmreports-frontend) и backend (http://localhost:8000).

  • backend: Node.js-приложение на порту 8000, зависит от Keycloak для верификации токенов.

  • networks: Объединяет сервисы в сеть sprint-8-network.

Frontend-интеграция

Фронт-система (reports-frontend) использует React и библиотеку @react-keycloak/web для OIDC-аутентификации. Вот код App.tsx:

import React from 'react';
import { ReactKeycloakProvider } from '@react-keycloak/web';
import Keycloak, { KeycloakConfig } from 'keycloak-js';
import ReportPage from './components/ReportPage';

const keycloakConfig: KeycloakConfig = {
  url: process.env.REACT_APP_KEYCLOAK_URL,
  realm: process.env.REACT_APP_KEYCLOAK_REALM || "reports-realm",
  clientId: process.env.REACT_APP_KEYCLOAK_CLIENT_ID || "reports-frontend"
};

const keycloak = new Keycloak(keycloakConfig);

const App: React.FC = () => {
  return (
    <ReactKeycloakProvider
      authClient={keycloak}
      initOptions={{
        onLoad: 'login-required',
        pkceMethod: 'S256'
      }}
    >
      <div className="App">
        <ReportPage />
      </div>
    </ReactKeycloakProvider>
  );
};

export default App;

Пояснения к коду:

  • keycloakConfig: Задаёт параметры подключения к Keycloak: URL (http://localhost:8080), realm (reports-realm), client (reports-frontend).

  • ReactKeycloakProvider: Оборачивает приложение, обеспечивая OIDC-аутентификацию.

  • initOptionsonLoad: 'login-required' требует логин при входе; pkceMethod: 'S256' включает PKCE для защиты.

  • ReportPage: Компонент, отображающий интерфейс отчётов.

Компонент ReportPage

Компонент ReportPage.tsx отображает интерфейс и запрашивает отчёт через /reports:

import React, { useState } from 'react';
import { useKeycloak } from '@react-keycloak/web';

const ReportPage: React.FC = () => {
  const { keycloak, initialized } = useKeycloak();
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [reportData, setReportData] = useState<any | null>(null);

  const downloadReport = async () => {
    if (!keycloak?.token) {
      setError('Not authenticated');
      return;
    }

    try {
      setLoading(true);
      setError(null);

      const response = await fetch(`${process.env.REACT_APP_API_URL}/reports`, {
        headers: {
          'Authorization': `Bearer ${keycloak.token}`
        }
      });

      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }

      const data = await response.json();
      setReportData(data);
      console.log('Report data:', data);
    } catch (err) {
      setError(err instanceof Error ? err.message : 'An error occurred');
    } finally {
      setLoading(false);
    }
  };

  if (!initialized) {
    return <div>Loading...</div>;
  }

  if (!keycloak.authenticated) {
    return (
      <div className="flex flex-col items-center justify-center min-h-screen bg-gray-100">
        <button
          onClick={() => keycloak.login()}
          className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
        >
          Login
        </button>
      </div>
    );
  }

  return (
    <div className="flex flex-col items-center justify-center min-h-screen bg-gray-100">
      <div className="p-8 bg-white rounded-lg shadow-md">
        <h1 className="text-2xl font-bold mb-6">Usage Reports</h1>
        
        <button
          onClick={downloadReport}
          disabled={loading}
          className={`px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 ${
            loading ? 'opacity-50 cursor-not-allowed' : ''
          }`}
        >
          {loading ? 'Generating Report...' : 'Download Report'}
        </button>

        {error && (
          <div className="mt-4 p-4 bg-red-100 text-red-700 rounded">
            {error}
          </div>
        )}

        {reportData && (
          <div className="mt-4 p-4 bg-green-100 text-green-700 rounded">
            <h2 className="text-lg font-bold">Report Data</h2>
            <p>ID: {reportData.id}</p>
            <p>Title: {reportData.title}</p>
            <p>Content: {reportData.content}</p>
            <p>User: {reportData.user}</p>
          </div>
        )}
      </div>
    </div>
  );
};

export default ReportPage;

Пояснения к коду:

  • useKeycloak: Хук из @react-keycloak/web, предоставляет доступ к состоянию Keycloak (keycloak.tokeninitializedauthenticated).

  • downloadReport: Отправляет запрос на /reports с JWT-токеном в заголовке Authorization: Bearer ${keycloak.token}. Если токен отсутствует, выводит ошибку.

  • Состояниеloading показывает процесс загрузки, error отображает ошибки (например, 403, если нет роли report_user), reportData хранит ответ от /reports.

  • Интерфейс: Если пользователь не аутентифицирован, показывается кнопка "Login". После логина — кнопка "Download Report" и отображение данных отчёта (например, { id: 123, title: "Usage Report 45" }).

  • fetch: Запрашивает /reports, ожидая JSON. Проверяет response.ok и обрабатывает ошибки.

Backend-интеграция

API отчётов (reports-api) проверяет JWT-токены. Вот код server.js:

const express = require('express');
const jwt = require('jsonwebtoken');
const jwksClient = require('jwks-rsa');
const cors = require('cors');

const app = express();

app.use(cors({
  origin: 'http://localhost:3000',
  methods: ['GET', 'POST', 'OPTIONS'],
  allowedHeaders: ['Content-Type', 'Authorization'],
}));

app.use(express.json());

app.use((req, res, next) => {
  console.log(`Received ${req.method} request to ${req.url}`);
  next();
});

const keycloakRealm = 'reports-realm';
const keycloakUrl = 'http://keycloak:8080';
const issuerUrl = 'http://localhost:8080';
const jwksUri = `${keycloakUrl}/realms/${keycloakRealm}/protocol/openid-connect/certs`;

const client = jwksClient({
  jwksUri: jwksUri,
});

function getKey(header, callback) {
  client.getSigningKey(header.kid, (err, key) => {
    if (err) {
      console.error('Error fetching signing key:', err);
      callback(err, null);
    } else {
      const signingKey = key?.getPublicKey();
      callback(null, signingKey);
    }
  });
}

const verifyToken = (req, res, next) => {
  const authHeader = req.headers['authorization'];
  if (!authHeader) {
    console.log('No Authorization header provided for', req.url);
    return res.status(401).json({ error: 'No token provided' });
  }

  const token = authHeader.split(' ')[1];
  if (!token) {
    console.log('Invalid token format for', req.url);
    return res.status(401).json({ error: 'Invalid token format' });
  }

  console.log('Token for', req.url, ':', token);

  jwt.verify(token, getKey, {
    issuer: `${issuerUrl}/realms/${keycloakRealm}`,
    algorithms: ['RS256'],
  }, (err, decoded) => {
    if (err) {
      console.error('Token verification failed for', req.url, ':', err.message);
      return res.status(401).json({ error: 'Token verification failed', details: err.message });
    }

    if (decoded.azp !== 'reports-frontend') {
      console.log('Invalid azp for', req.url, ':', decoded.azp);
      return res.status(401).json({ error: 'Invalid client', details: 'azp does not match reports-frontend' });
    }

    if (!decoded.realm_access?.roles.includes('report_user')) {
      console.log('User does not have required role for', req.url, ':', decoded.realm_access?.roles);
      return res.status(403).json({ error: 'Insufficient permissions', details: 'report_user role required' });
    }

    console.log('Decoded token for', req.url, ':', decoded);
    req.user = decoded;
    next();
  });
};

app.get('/reports', verifyToken, (req, res) => {
  const report = {
    id: Math.floor(Math.random() * 1000),
    title: `Usage Report ${Math.floor(Math.random() * 100)}`,
    content: `This is a sample report generated on ${new Date().toISOString()}.`,
    user: req.user.preferred_username,
  };
  res.json(report);
});

app.listen(8000, () => console.log('Backend running on port 8000'));

Пояснения к коду:

  • CORS: Разрешает запросы с http://localhost:3000 (фронт-система), чтобы избежать ошибок кросс-доменного доступа.

  • Логированиеapp.use((req, res, next)) выводит в консоль все запросы для отладки.

  • Keycloak настройкиkeycloakRealmkeycloakUrljwksUri указывают realm (reports-realm) и адрес Keycloak для получения публичных ключей (JWKS).

  • getKey: Получает публичный ключ для проверки подписи JWT.

  • verifyToken: Middleware проверяет JWT:

    • Извлекает токен из Authorization: Bearer <token>.

    • Проверяет подпись с помощью JWKS (jwt.verify).

    • Убеждается, что токен выдан для reports-frontend (decoded.azp).

    • Проверяет наличие роли report_user в realm_access.roles.

    • Сохраняет декодированный токен в req.user.

  • Эндпоинт /reports: Возвращает данные отчёта (например, { id: 123, title: "Usage Report 45" }) только для пользователей с ролью report_user.

Тестирование

Запустите проект:

docker compose up -d

Шаги тестирования:

  1. Откройте http://localhost:3000, введите access1/accesspass1.

  2. В DevTools (вкладка Network) найдите запрос к /reports, проверьте заголовок Authorization: Bearer <JWT>.

  3. Декодируйте JWT на jwt.io, убедитесь, что он содержит realm_access.roles: ["report_user"]sub: access1.

  4. Проверьте ответ от /reports (например, { "id": 123, "title": "Usage Report 45" }).

Страница логина Keycloak с формой ввода access3
Страница логина Keycloak с формой ввода access3
 DevTools браузера (вкладка Network), показывающий запрос к /reports 
DevTools браузера (вкладка Network), показывающий запрос к /reports 
Ответ от /reports в браузере, показывающий данные отчёта
Ответ от /reports в браузере, показывающий данные отчёта

Проблемы и решения

  • CORS: Если фронт-система не получает ответ, проверьте CORS в server.js (origin: 'http://localhost:3000').

  • JWT: Если токен не содержит report_user, проверьте маппинг ролей в Keycloak ("Role Mappings").

  • Логи: Используйте консольные логи в server.js для отладки (например, ошибки верификации).

9. Решение исходной задачи: интеграция платформ через Keycloak

Мы начали с задачи: обеспечить доступ из фронт-системы (reports-frontend) к API отчётов (reports-api) через общекорпоративный Keycloak, чтобы JWT-токен содержал роль report_user. В контексте e-commerce это можно представить как интерфейс интернет-магазина, где менеджеры запрашивают отчёты о продажах, а Keycloak управляет аутентификацией через Single Sign-On (SSO). После изучения SSO, OIDC, Keycloak и практического примера (часть 8) мы готовы показать, как системный аналитик решает эту задачу. В крупных компаниях аналитик не настраивает Keycloak сам, а подаёт заявку (например, в Jira) и координирует с администраторами Keycloak, разработчиками фронт-системы и API, а также владельцами бизнес-логики. Непонимание OpenID Connect (OIDC) или Keycloak может привести к ошибкам в заявке (например, отсутствие роли report_user в токене) или задержкам из-за неясности, с кем обсуждать детали.

Для наглядности начнём с диаграммы, показывающей поток доступа:

Эта схема, показывает: пользователь логинится через Keycloak, reports-frontend получает JWT с ролью report_user, а reports-api проверяет токен для доступа
Эта схема, показывает: пользователь логинится через Keycloak, reports-frontend получает JWT с ролью report_user, а reports-api проверяет токен для доступа

Аналитика: подача заявки и координация

В крупных системах аналитик инициирует заявку на настройку Keycloak, а не выполняет её сам. Это требует чёткого описания требований и взаимодействия с командами. Без понимания OIDC и Keycloak заявка может быть неполной, что приведёт к ошибкам (например, токен без report_user). Вот шаги аналитики:

  1. Сбор требований:

    • С владельцами API отчётов: Какие роли нужны? Например, report_user для доступа к отчётам, administrator для управления.

    • Какие данные в JWT? Роль report_user обязательна, возможно, кастомные атрибуты (например, department для фильтрации отчётов).

    • Риски: Неправильные роли (доступ к чужим отчётам), отсутствие атрибутов, низкая производительность.

  2. Подача заявки:

    • Для админов Keycloak: Включить в заявку:

      • Создание realm reports-realm (или использование существующего).

      • Добавление пользователей (access1) или синхронизацию с Active Directory (AD).

      • Создание роли report_user и её маппинг на пользователей.

      • Настройку клиентов: reports-frontend (public, redirect URI: http://localhost:3000/*, PKCE: Enforced), reports-api (bearer-only, с секретом oNwoLQdvJAvRcL89SydqCWCe5ry1jMgq).

    • Пример текста заявки:

      Создать realm reports-realm. Добавить пользователя access1 с ролью report_user. Настроить клиенты:
      - reports-frontend: public, redirect URI http://localhost:3000/*, включить PKCE (S256).
      - reports-api: bearer-only, секрет oNwoLQdvJAvRcL89SydqCWCe5ry1jMgq.
  3. Координация с командами:

    • Разработчики фронт-системы: Убедиться, что reports-frontend использует OIDC с PKCE (как в App.tsxReportPage.tsx, часть 8).

    • Разработчики API отчётов: Настроить верификацию JWT с проверкой report_user (как в server.js, часть 8).

    • DevOps: Подтвердить запуск Keycloak и приложений (как в docker-compose.yml, часть 8).

  4. Проверка инфраструктуры:

    • Источник пользователей: AD (для корпоративных пользователей) или PostgreSQL (для прототипа)?

    • Безопасность: HTTPS для всех запросов, PKCE для reports-frontend, MFA для продакшена.

    • Масштабируемость: Поддерживает ли Keycloak нагрузку от множества пользователей?

Схема процесса аналитики:

Эта схема показывает, как аналитик координирует настройку через заявку и тестирование.
Эта схема показывает, как аналитик координирует настройку через заявку и тестирование.

Аналитик должен:

  • С владельцами API: Уточнить роли (report_user).

  • С админами Keycloak: Подать заявку на настройку reports-realm, ролей, клиентов.

  • С разработчиками: Проверить OIDC, PKCE, верификацию JWT.

  • Тестировать: Декодировать JWT в DevTools, проверить доступ к /reports.

10. Заключение

Мы начали с задачи: обеспечить доступ из фронт-системы (reports-frontend) к API отчётов (reports-api) через общекорпоративный Keycloak, чтобы JWT-токен содержал роль report_user. В контексте e-commerce это можно представить как интерфейс интернет-магазина, где менеджеры запрашивают отчёты о продажах, а Keycloak управляет аутентификацией через Single Sign-On (SSO). Разобрав принципы SSO, OIDC, компоненты Keycloak (realms, хранилища, PKCE) и практический пример (часть 8), мы показали, как системный аналитик решает эту задачу через заявки и координацию с командами: администраторами Keycloak, разработчиками фронт-системы и API, а также владельцами бизнес-логики.

Для наглядности вот финальная схема, показывающая роль аналитика в координации:

Эта схема показывает: аналитик подаёт заявку на настройку reports-realm, согласовывает роли, и тестирует JWT в DevTools, чтобы убедиться, что доступ работает.
Эта схема показывает: аналитик подаёт заявку на настройку reports-realm, согласовывает роли, и тестирует JWT в DevTools, чтобы убедиться, что доступ работает.

Итоги

Keycloak упрощает задачу интеграции платформ через SSO:

  • Централизация: Пользователи логинятся один раз через reports-realm, получая доступ к reports-frontend и reports-api.

  • Безопасность: Роль report_user в JWT обеспечивает доступ, а PKCE и HTTPS защищают от атак.

  • Гибкость: Keycloak поддерживает PostgreSQL или Active Directory для хранения пользователей.

Для аналитика ключевые уроки:

  • Чёткая заявка: Указывайте в заявке (например, в Jira) создание reports-realm, добавление пользователей (access1), ролей (report_user), клиентов (reports-frontendreports-api), и включение PKCE.

  • Координация: Обсуждайте с владельцами API, какие роли нужны, с разработчиками — интеграцию OIDC, с админами — настройку Keycloak.

  • Тестирование: Проверяйте JWT в DevTools (как в частях 8–9), декодируя токен на jwt.io, чтобы убедиться, что он содержит realm_access.roles: ["report_user"].

Типичные проблемы

  • Непонимание OIDC: Аналитик может не знать, какие данные (роли, атрибуты) нужны в JWT или с кем их обсуждать.

  • Ошибки в заявке: Неполные требования (например, отсутствие report_user) приводят к неправильным токенам.

  • Задержки: Несогласованность между командами тормозит настройку.

Что дальше

Для улучшения интеграции:

  • MFA: Включите многофакторную аутентификацию в Keycloak для продакшена.

  • AD-интеграция: Настройте User Federation для синхронизации пользователей из Active Directory.

  • Документация: Ведите реестр ролей и клиентов для аудита.

  • Тестирование: Используйте penetration testing для проверки безопасности.

Аналитикам стоит изучить документацию Keycloak (https://www.keycloak.org/docs) и попробовать пример из части 8: запустите проект, подайте тестовую заявку на настройку reports-realm, и проверьте JWT в DevTools. Это поможет уверенно решать задачи интеграции платформ!

Комментарии (2)


  1. svetayet
    14.07.2025 14:08

    отличная исчерпывающая статья, какие уже редко попадаются. спасибо за труд!


    1. B4sil Автор
      14.07.2025 14:08

      Приятно! Спасибо)