Вы поставили Keycloak, открыли админку — и сразу налетела куча незнакомых слов: realm, client, client scope, mapper, группы, роли. Непонятно, что главнее, что во что вложено и с чего начинать. А пока модель в голове не уложилась, легко настроить так, что вход то работает, то нет, роли «не доезжают» до приложения, а почему — неясно. Давайте разберём модель Keycloak по кусочкам, не торопясь, с аналогиями из жизни, и в конце соберём минимальный рабочий набор для типичной связки «серверный бэкенд + браузерный фронтенд».
Зачем вообще отдельный сервер для входа
Прежде чем разбирать внутренности Keycloak, стоит понять, какую боль он лечит — иначе все эти realm и client кажутся лишним усложнением.
Представьте, что у компании одно приложение. Оно само хранит пароли пользователей, само проверяет логин, само решает, кому что можно. Пока приложение одно — терпимо. Но приложений становится два, потом пять. И начинается:
- пользователя надо завести отдельно в каждом приложении;
- человек поменял пароль в одном — в остальных остался старый;
- единого входа нет: залогинился в одном приложении, в соседнее заходишь заново;
- права (кто админ, кто обычный) описаны в каждом по-своему.
Keycloak — это отдельный сервер, который забирает всю эту работу себе. Он один на всю компанию хранит пользователей, проверяет пароли, умеет вход через Google или корпоративную учётную запись. А приложениям он выдаёт подтверждение: «да, это действительно Иван, и вот какие у него права». Приложения больше не возятся с паролями — они только спрашивают Keycloak и доверяют его ответу.
Ответ Keycloak отдаёт не в виде «да/нет», а в виде токена — подписанной строки, внутри которой записано, кто пользователь и что ему можно. Приложение проверяет подпись (подделать её без секретного ключа Keycloak нельзя) и доверяет содержимому, не дёргая Keycloak на каждый запрос. Это важная мысль, к которой мы ещё вернёмся: приложение читает права прямо из токена, а не ходит за ними в Keycloak.
Чтобы было видно, кто с кем общается, вот общая картина. На схеме: пользователь работает с приложением, приложение отправляет его логиниться в Keycloak, получает обратно токены и дальше ходит с ними в свой API.
Теперь разберём, из чего Keycloak состоит внутри.
Realm — изолированное пространство
Самое верхнее понятие в Keycloak — это realm. С него начинается всё остальное, поэтому с него и начнём.
Представьте многоквартирный дом. Один установленный Keycloak — это весь дом. А realm — это отдельная квартира со своим замком. Жильцы одной квартиры физически не могут попасть в другую: у каждой квартиры свои ключи, свои жильцы, свои правила. Квартиры стоят в одном доме, но друг про друга ничего не знают.
Realm — это полностью изолированное пространство. Внутри него живут: свой набор пользователей, свои приложения (client'ы), свои роли, группы и настройки входа (например, через какие способы можно логиниться). Главное свойство — изоляция: пользователь, заведённый в одном realm, в другом realm просто не существует. Роль из одного realm в другом не действует. Это не «разные папки в одной системе», а действительно отдельные миры.
Зачем такая жёсткая изоляция? Затем, что на одном сервере Keycloak часто нужно держать совершенно разные аудитории, которые не должны пересекаться. Типичные примеры:
- отдельно realm для сотрудников компании и отдельно realm для внешних клиентов — у них разные пользователи и разные правила;
- отдельный realm под каждый независимый проект, чтобы они не мешали друг другу.
На схеме видно, что один Keycloak держит несколько realm, и они не связаны: пользователи и роли realm A никак не видны в realm B.
Важная деталь, на которой спотыкаются новички: в Keycloak всегда есть встроенный realm с именем master. Возникает соблазн складывать пользователей приложения прямо в него — он же уже готовый. Так делать не надо. master нужен только для администрирования самого Keycloak: в нём живут учётки, которыми вы заходите в админку и управляете другими realm. Обычных пользователей вашего приложения туда не кладут. Под свой проект всегда создают новый realm — например, myapp.
Ещё одна вещь, которую полезно запомнить сразу: все адреса Keycloak привязаны к realm. Базовый адрес, по которому приложение находит ключи и настройки конкретного realm, выглядит так:
https://auth.example.com/realms/myapp
Этот адрес называют issuer (издатель токенов) — именно он стоит в каждом выданном токене как «кто меня выпустил». Запомните его: он понадобится при настройке приложения, и он у каждого realm свой.
Client — это приложение, которое обращается к Keycloak
Внутри realm живут client'ы. Это второе по важности понятие, и оно часто путает, потому что слово «client» здесь означает не пользователя, а приложение.
Откуда вообще берётся такое понятие? Keycloak должен как-то различать, какое именно приложение к нему пришло. Браузерный фронтенд, мобильное приложение и фоновый серверный сервис ведут себя по-разному, и доверять им можно по-разному. Чтобы их различать, для каждого приложения в realm заводят запись.
Client — это и есть запись о приложении внутри realm. У каждого приложения, которое пользуется этим Keycloak, есть свой client со своим идентификатором (client id) — например, myapp-frontend или myapp-backend. Когда приложение начинает процесс входа, оно представляется этим идентификатором: «здравствуй, Keycloak, я client myapp-frontend».
Ключевая характеристика, по которой client'ы делятся на два типа, — может ли приложение надёжно хранить секрет (то есть пароль самого приложения). От этого зависит, как именно приложение доказывает Keycloak, что оно — это оно.
Public client
Public («публичный») — это приложение, которое в принципе не может надёжно спрятать секрет. Почему не может? Потому что весь его код доступен пользователю:
- код одностраничного сайта (React, Angular, Vue) целиком загружается в браузер — открой инструменты разработчика и читай;
- мобильное приложение можно скачать и распаковать.
Любой «секретный пароль», зашитый в такой код, на самом деле виден всем желающим, а значит, он бесполезен как секрет. Поэтому у public client секрета нет вообще — Keycloak его и не требует.
Так настраивают: одностраничные сайты (React, Angular, Vue) и мобильные приложения.
Возникает резонный вопрос: если у приложения нет секрета, как тогда защитить вход от подмены? Для этого у public client обязательно используют PKCE. Идея простая: в самом начале входа приложение генерирует одноразовую секретную строку, держит её у себя и в конце предъявляет, доказывая, что именно оно — то самое приложение, которое начинало вход. Это закрывает дыру, через которую злоумышленник мог бы перехватить код авторизации и обменять его на токены вместо вас. В современных версиях Keycloak PKCE для public client включён по умолчанию, так что чаще всего ничего настраивать не нужно — просто знайте, что он работает.
Confidential client
Confidential («конфиденциальный») — это приложение, которое работает на сервере и может надёжно хранить секрет, потому что его код пользователю не виден: он крутится на вашем бэкенде, а не в браузере.
У такого client есть client secret — по сути, пароль самого приложения. Им бэкенд доказывает Keycloak: «я действительно client myapp-backend, вот мой секрет». Так настраивают серверные приложения: бэкенд на Spring Boot, сервис на Node.js и т. п.
Разница между двумя типами укладывается в одну табличку. На ней — кто чем доказывает свою подлинность и для каких приложений подходит.
| Тип client | Хранит секрет | Чем доказывает подлинность | Для каких приложений |
|---|---|---|---|
| public | нет | PKCE | фронтенд (SPA), мобильные |
| confidential | да | client secret | серверный бэкенд |
Простое правило, которое стоит запомнить: код виден пользователю — public; код только на сервере — confidential.
Пользователи
Мы разобрали, где живут приложения. Теперь — про людей.
Пользователь (user) — это учётная запись человека внутри realm. У неё есть логин, пароль, email, имя, а также произвольные дополнительные поля, которые в Keycloak называют атрибутами (например, отдел или номер телефона). Именно пользователь проходит вход: вводит логин и пароль, а Keycloak их проверяет.
Откуда берутся пользователи? Способов несколько:
- их создают вручную в админке Keycloak;
- они регистрируются сами, если регистрация включена;
- их подтягивают из внешнего каталога компании (например, LDAP или Active Directory);
- они приходят из входа через Google и подобные внешние сервисы.
Важный момент: сам по себе пользователь никаких прав не несёт. «Иван существует» — это ещё не «Ивану что-то можно». Права пользователю дают роли — к ним и переходим.
Роли: realm-роли против client-ролей
Вот центральная развилка модели, которую важно понять как следует.
Проблема: запись «пользователь Иван» не отвечает на вопрос «что Ивану можно». Нам нужен способ сказать «Иван — администратор» или «Иван может оформлять заказы». Именно для этого существуют роли.
Роль — это метка с правом, которую вешают на пользователя. admin, manager, customer — типичные роли. Дальше приложение смотрит на роли в токене и решает, пускать ли пользователя в ту или иную часть. Например, в раздел администрирования пускаем только тех, у кого есть роль admin.
В Keycloak роли бывают двух видов, и разница между ними — частый источник путаницы, поэтому разберём её подробно.
Realm-роль — это роль, общая для всего realm. Она не привязана ни к какому конкретному приложению и имеет смысл во всём проекте сразу. Хорошие кандидаты в realm-роли — это общие, сквозные понятия: admin, user, manager. Если у вас одно приложение или несколько приложений, но право означает одно и то же везде, — это realm-роль.
Client-роль — это роль, которая принадлежит конкретному client (приложению). Она существует только в контексте этого приложения. Зачем такое нужно? Затем, что одно и то же слово в разных приложениях может значить разное. Роль manager в приложении myapp-backend и роль manager в приложении reports-app — это две независимые роли, они не пересекаются. У «менеджера» в основном приложении и «менеджера» в системе отчётов могут быть совершенно разные права, и client-роли позволяют их не смешивать.
Аналогия, которая хорошо ложится: realm-роль — это пропуск во всё здание, а client-роль — ключ от одной конкретной комнаты внутри этого здания.
Когда какие выбирать? Для маленького проекта обычно хватает realm-ролей — они проще: завёл admin и user, раздал пользователям, и всё. Client-роли берут, когда приложений несколько и права в них реально различаются настолько, что общие realm-роли начинают мешать.
Отдельно полезно знать про составные роли (composite). Это роль, которая включает в себя другие роли. Назначаешь пользователю одну такую роль — и вместе с ней он автоматически получает все вложенные. Классический пример: роль admin включает в себя роль user. Тогда любому администратору не нужно отдельно выдавать user — она приедет вместе с admin. Удобно, чтобы не дублировать назначения.
Группы — чтобы не раздавать роли по одной
Роли мы умеем раздавать пользователям поштучно. Но представьте: у вас 200 пользователей, и каждому надо назначить один и тот же набор из пяти ролей. Делать это руками, по одному человеку, — мучительно и легко где-нибудь ошибиться (кому-то роль забыли, кому-то лишнюю дали).
Эту боль решают группы.
Группа (group) — это набор пользователей с общими ролями и атрибутами. Логика такая: вы назначаете роли группе один раз, а каждый пользователь, который попадает в эту группу, автоматически получает все её роли. Добавил человека в группу — он сразу получил нужные права. Перевёл в другую группу — набор прав поменялся. Не надо трогать роли у каждого вручную.
Группы можно вкладывать друг в друга: подгруппа наследует роли своей родительской группы. Например, группа «Сотрудники» даёт базовые права, а вложенная в неё группа «Бухгалтерия» добавляет ещё свои — и член бухгалтерии получает и то, и другое.
Чтобы не путать роли и группы, запомните разницу одной фразой: роль — это само право; группа — это удобный способ раздать пачку прав сразу многим пользователям.
Теперь соберём всю модель в одну картину. На схеме видно дерево: realm — корень, внутри него лежат client'ы, пользователи, группы и realm-роли; а client-роли «висят» уже под конкретным client'ом, не на уровне realm.
Пунктирные стрелки на схеме читаются так: группы раздают своим участникам роли и содержат пользователей. Сплошные стрелки — это «что во что вложено».
Как роли попадают в токен
А вот частая точка, где всё ломается на практике. Вы назначили пользователю роль в админке, всё выглядит правильно — а приложение её «не видит» и не пускает. Причина почти всегда одна и та же: роль не попала в токен.
Вспомним мысль из начала статьи: токен — это не запрос в базу Keycloak в реальном времени. Это готовая, уже подписанная «справка», и приложение читает права прямо из неё, не ходя в Keycloak. А значит, нужные данные должны оказаться внутри токена в тот момент, когда Keycloak его выдаёт. Если роли в токене нет — приложению неоткуда о ней узнать, сколько бы ролей ни висело на пользователе в админке.
За то, что и в каком виде попадает в токен, отвечают два механизма.
Протокол-маппер (protocol mapper) — это правило вида «возьми вот это (роль, атрибут, email) и положи в токен под таким-то именем». Хорошая новость: мапперы для realm-ролей и для client-ролей в Keycloak есть из коробки и обычно включены по умолчанию. То есть в типовой настройке роли попадают в токен сами, без ручной работы. Но если кто-то их выключил или вы настраивали client вручную «с нуля» — вот тут роли и могут потеряться.
Client scope — это переиспользуемый набор мапперов. Вместо того чтобы вешать одни и те же мапперы на каждый client руками, их собирают в 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 — буквально «сервер ресурсов». Оно само не занимается входом (логином, паролями, перенаправлениями) — этим занят фронтенд вместе с Keycloak. Бэкенду приходит уже готовый токен в заголовке Authorization: Bearer <access_token>, и его задача — только проверить этот токен и пустить или не пустить.
Минимум настройки для Spring Boot — указать issuer вашего realm:
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://auth.example.com/realms/myapp
Что происходит дальше автоматически. По этому адресу Spring сам находит служебный файл .well-known/openid-configuration (его публикует Keycloak), оттуда узнаёт адрес с публичными ключами realm (JWKS), скачивает ключи и кэширует их у себя. После этого каждый входящий токен Spring проверяет локально: сходится ли подпись (теми самыми ключами) и тот ли издатель (issuer). Никакого обращения к Keycloak на каждый запрос не происходит — поэтому это быстро.
Одна типичная доработка, без которой роли не заработают. Spring по умолчанию не знает, что роли лежат именно в realm_access.roles — это формат, специфичный для Keycloak, а не общий стандарт. Поэтому к нему добавляют небольшой конвертер ролей: он достаёт роли из realm_access.roles и превращает их в привычные Spring права вида ROLE_admin. После этого работают обычные проверки доступа на эндпоинтах. Подробно этот конвертер разбирается в отдельной статье про защиту API — ссылка ниже.
Минимум для связки «бэкенд + фронт»
Теперь, когда модель понятна по частям, соберём её в практический чек-лист. Для типичного приложения с серверным бэкендом и браузерным фронтендом нужно:
- Один realm под проект — например
myapp(именно свой, неmaster). - Два client'а в этом realm:
myapp-frontend— public, с PKCE, для одностраничного фронтенда;myapp-backend— confidential, с client secret, для серверного API.
- Роли — для начала realm-роли, например
adminиuser. Client-роли подключают позже, если приложений станет несколько и права начнут различаться. - Группы — по желанию: удобно, когда пользователей много и наборы прав типовые.
- Пользователи с назначенными ролями — напрямую или через группы.
- Проверить, что realm-роли реально попадают в токен (поле
realm_access.roles) — мапперы для этого обычно уже включены, но убедиться стоит. - На бэкенде — 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; ключи (JWKS) он находит сам через.well-known/openid-configurationи проверяет токены локально. - Минимум для бэкенд+фронт: один realm, public + confidential client, realm-роли, пользователи.
Что почитать дальше
- Что такое OAuth2 и OpenID Connect — какими протоколами Keycloak выдаёт токены и чем отличаются access_token, id_token и refresh_token.
- Защита Spring Boot API с Keycloak — resource server, конвертер ролей и проверки доступа на эндпоинтах подробно.