← назад к разделу

Вы поставили Keycloak, зашли в админку — и сразу куча незнакомых слов: realm, client, client scope, mapper. Без понимания, что это и как связано, легко настроить так, что вход то работает, то нет, а роли не доезжают до приложения. Разберём модель Keycloak по кусочкам и в конце соберём минимальный набор для типичной связки «бэкенд + фронт».

Зачем вообще отдельный сервер для входа

Раньше каждое приложение само хранило пароли, само проверяло логин, само раздавало права. Когда приложений становится несколько, начинается боль: пользователь заводится в каждом отдельно, пароль меняется в одном месте, а в других старый, и единого входа нет.

Keycloak — это отдельный сервер, который берёт всю эту работу на себя. Он хранит пользователей, проверяет пароли, умеет вход через Google или корпоративный логин, выдаёт приложениям подтверждение «да, это вправду Иван, и вот его права». Приложения перестают заниматься паролями — они только спрашивают Keycloak и доверяют его ответу.

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

Realm — изолированное пространство

Представьте многоквартирный дом. Один Keycloak — это дом, а realm — отдельная квартира со своим замком. Жильцы одной квартиры не имеют доступа в другую, у каждой свои ключи и свои правила.

Realm — это полностью изолированное пространство: свой набор пользователей, своих приложений, ролей, групп и настроек входа. Пользователь из одного realm не существует в другом. Это удобно, когда на одном сервере нужно держать, например, отдельно сотрудников и отдельно клиентов, или несколько независимых проектов.

Важная деталь: в Keycloak всегда есть встроенный realm с именем master. Он нужен только для администрирования самого Keycloak — туда не кладут обычных пользователей приложения. Под свой проект всегда создают новый realm, например myapp.

Все адреса Keycloak привязаны к realm. Базовый адрес, по которому приложение находит ключи и настройки, выглядит так:

https://auth.example.com/realms/myapp

Этот адрес называют issuer (издатель токенов) — запомните его, он понадобится при настройке приложения.

Client — это приложение, которое спрашивает Keycloak

Проблема: Keycloak должен как-то понимать, какое именно приложение к нему пришло. Браузерный фронтенд, мобильное приложение и фоновый сервис ведут себя по-разному, и доверять им можно по-разному.

Client — это запись о приложении внутри realm. У каждого приложения, которое пользуется Keycloak, есть свой client со своим идентификатором (client id), например myapp-frontend или myapp-backend. Когда приложение начинает вход, оно представляется этим идентификатором.

Ключевая характеристика client — может ли он хранить секрет. Отсюда два типа.

Public client

Public («публичный») — приложение, которое не может надёжно спрятать пароль. Код фронтенда целиком уезжает в браузер, мобильное приложение можно распаковать — любой «секрет» внутри них на самом деле виден всем. Поэтому у public client секрета нет вообще.

Так настраивают: одностраничные сайты (React, Angular, Vue), мобильные приложения.

Чтобы такой вход всё равно был безопасным, для public client обязательно используют PKCE — механизм, при котором приложение на старте входа генерирует одноразовую секретную строку и доказывает ею, что именно оно завершает вход. Это защищает от перехвата кода авторизации. В современных версиях Keycloak PKCE для public client включён по умолчанию.

Confidential client

Confidential («конфиденциальный») — приложение, которое работает на сервере и может надёжно хранить секрет, потому что его код не виден пользователю.

У такого client есть client secret — пароль самого приложения. Им бэкенд доказывает Keycloak, что он — это он. Так настраивают серверные приложения: бэкенд на Spring Boot, Node.js и т. п.

public client     →  секрета нет, защита через PKCE   →  фронтенд, мобильное
confidential      →  есть client secret               →  серверный бэкенд

Простое правило: код виден пользователю — public; код только на сервере — confidential.

Пользователи

Пользователь (user) — это учётная запись человека внутри realm: логин, пароль, email, имя, произвольные дополнительные поля (атрибуты). Именно пользователь проходит вход.

Пользователей создают руками в админке, заводят через регистрацию, импортируют из внешнего каталога (например, LDAP) или подтягивают из входа через Google и подобных. Сами по себе пользователи прав не несут — права им дают роли, о которых дальше.

Роли: realm-роли против client-ролей

Проблема: «пользователь Иван» — это ещё не ответ на вопрос «что Ивану можно». Нужен способ сказать «Иван — администратор» или «Иван может оформлять заказы». Это и есть роли.

Роль — это метка с правом, которую вешают на пользователя. admin, manager, customer — типичные роли. Приложение потом смотрит на роли в токене и решает, пускать ли в ту или иную часть.

В Keycloak роли бывают двух видов, и разница важна.

  • Realm-роль — общая для всего realm. Подходит для прав, которые имеют смысл во всём проекте: admin, user.
  • Client-роль — принадлежит конкретному client (приложению). Подходит, когда одно и то же слово в разных приложениях значит разное. Например, роль manager в myapp-backend и manager в каком-нибудь reports-app — это две независимые роли, они не пересекаются.

Аналогия: realm-роль — это пропуск во всё здание, client-роль — ключ от одной конкретной комнаты.

Для маленького проекта обычно хватает realm-ролей — они проще. Client-роли берут, когда приложений несколько и права у них разные.

Роль можно сделать составной (composite): назначаешь одну роль — вместе с ней автоматически даются вложенные. Например, admin включает в себя user.

Группы — чтобы не раздавать роли по одной

Проблема: если у вас 200 пользователей и каждому надо назначить 5 одинаковых ролей, делать это руками по одному — мучительно и легко ошибиться.

Группа (group) — это набор пользователей с общими ролями и атрибутами. Назначаешь роли группе, а пользователи внутри неё получают их автоматически. Перевёл человека в другую группу — у него поменялся набор прав. Группы можно вкладывать друг в друга: подгруппа наследует роли родителя.

Разница в одной фразе: роль — это само право, группа — это удобный способ раздать пачку прав сразу многим.

Как роли попадают в токен

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

Токен — это не запрос в базу Keycloak. Это готовая подписанная «справка», и приложение читает права прямо из неё. Значит, нужные данные должны быть внутри токена в момент его выдачи. За то, что и в каком виде туда попадает, отвечают два механизма.

  • Протокол-маппер (protocol mapper) — правило «возьми вот это (роль, атрибут, email) и положи в токен под таким-то именем». Маппер для realm-ролей и маппер для client-ролей в Keycloak уже есть из коробки и обычно включены по умолчанию.
  • Client scope — переиспользуемый набор мапперов, который подключают к client'у, чтобы не настраивать одно и то же руками каждый раз.

Куда именно роли ложатся в токене, стоит знать наизусть — на эти поля смотрит приложение:

{
  "preferred_username": "ivan",
  "realm_access": {
    "roles": ["admin", "user"]
  },
  "resource_access": {
    "myapp-backend": {
      "roles": ["manager"]
    }
  }
}
  • realm-роли лежат в realm_access.roles;
  • client-роли — в resource_access.<client-id>.roles.

Если роль есть у пользователя, но в токене её нет — ищите проблему в мапперах или client scope, а не в назначении роли.

Как это читает приложение (Spring Boot)

Серверное приложение выступает в роли resource server — оно не занимается входом, а только проверяет уже выданный токен. Минимум настройки — указать issuer realm:

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://auth.example.com/realms/myapp

По этому адресу Spring сам находит файл .well-known/openid-configuration, оттуда — адрес с публичными ключами Keycloak (JWKS), скачивает ключи и кэширует их. Дальше каждый входящий токен он проверяет на подпись и на совпадение издателя — без обращения к Keycloak на каждый запрос.

Одна типичная доработка: Spring по умолчанию не знает, что роли лежат именно в realm_access.roles — это формат Keycloak, а не стандарт. Поэтому добавляют небольшой конвертер, который достаёт роли оттуда и превращает их в привычные Spring права (ROLE_admin и т. п.). После этого работают обычные проверки доступа на эндпоинтах.

Минимум для связки «бэкенд + фронт»

Соберём всё в практический набор. Для типичного приложения с серверным бэкендом и браузерным фронтендом нужно:

  1. Один realm под проект — например myapp (не master).
  2. Два client'а в этом realm:
    • myapp-frontendpublic, с PKCE, для одностраничного фронтенда;
    • myapp-backendconfidential, с client secret, для серверного API.
  3. Роли — для начала realm-роли, например admin и user. Client-роли подключают позже, если приложений станет несколько.
  4. Группы — по желанию: удобно, когда пользователей много и права типовые.
  5. Пользователи с назначенными ролями (напрямую или через группы).
  6. Проверить, что realm-роли попадают в токен (realm_access.roles) — мапперы для этого обычно уже включены.
  7. На бэкенде — resource server с issuer-uri на ваш realm и конвертером ролей.

Этого достаточно, чтобы фронт логинил пользователя через Keycloak, получал токен и слал его в API, а бэкенд проверял токен и пускал по ролям.

Коротко

  • Keycloak — отдельный сервер входа: хранит пользователей, проверяет пароли, выдаёт подписанные токены; приложения паролями не занимаются.
  • Realm — изолированное пространство (свои пользователи, приложения, роли). master — только для администрирования, под проект создают свой realm.
  • Client — запись о приложении. Public (нет секрета, защита через PKCE) — для фронтенда и мобильных; confidential (есть client secret) — для серверного бэкенда.
  • Пользователь проходит вход; права несут не пользователи, а роли.
  • Realm-роль — общая для всего realm; client-роль — принадлежит конкретному приложению.
  • Группа — способ раздать пачку ролей сразу многим пользователям.
  • В токен роли кладут протокол-мапперы (часто включены по умолчанию); realm-роли — в realm_access.roles, client-роли — в resource_access.<client-id>.roles.
  • Spring Boot как resource server настраивается одним issuer-uri; ключи находит сам через .well-known/openid-configuration.
  • Минимум для бэкенд+фронт: один realm, public + confidential client, realm-роли, пользователи.

Что почитать дальше

  • Что такое OAuth2 и OpenID Connect — какими протоколами Keycloak выдаёт токены.
  • Защита Spring Boot API с Keycloak — resource server, конвертер ролей и проверки на эндпоинтах подробно.