Динамическое управление доступом: Роли пользователей на лету с Spring Boot и Keycloak.
Салимжанов Р.Д.
Введение
В современном приложении крайне важно организовать безопасный доступ пользователей к различным ресурсам в зависимости от их роли. В этой статье мы рассмотрим, как реализовать ролевую модель доступа, используя Spring Boot, PostgreSQL и Keycloak в качестве сервера авторизации.
В предыдущей статье я уже разбирал, как настроить Keycloak и Spring Security на Java. На основе того приложения мы добавим ролевую модель, так чтобы роли пользователей извлекались динамически из базы данных PostgreSQL, а не хранились статически в токенах. Такой подход обеспечит большую гибкость и безопасность, позволяя изменять роли пользователей без необходимости пересоздания токенов.
Предыдущая статья:
Настройка Keycloak, базы данных и приложения.
Итак, я не вносил изменений в файл docker-compose.yml, который мы будем использовать для запуска Keycloak. Как и в прошлый раз, запускаем контейнер с помощью команды docker-compose up . После успешного запуска контейнера вы сможете получить доступ к Keycloak по адресу http://localhost:8080. Далее мы можем продолжить настройку Keycloak в соответствии с требованиями.
Если вдруг он уже настроен, вы можете работать в нем, или удалить контейнеры, сети, образы и данные, чтобы очистить окружение. Выбор за вами. Я почистил и настроил заново:
Realm name: app_realm
Client ID: spring-app
Users: test_user
Email: test@example.com
Password: test123
Подробную настройку можно почитать в моей предыдущей статье.
Далее Настройка Mappers (Мапперов токена).
Более подробно мы рассмотрим, как настроить мапперы токена в Keycloak для добавления свойства preferred_username в токен доступа. Это необходимо для того, чтобы приложение могло получать и использовать имя пользователя в удобном формате, что упрощает управление доступом и персонализацию пользовательского опыта.
1)В клиенте spring-app перейдите на вкладку "Client Scopes": Это позволит вам управлять областями доступа, которые определяют, какие данные будут включены в токены для вашего клиента.
2)Выберите "spring-app-dedicated" вашего клиента: Выбор конкретной области доступа гарантирует, что изменения будут применены только к нужному клиенту.
3)Нажмите "Add mapper(By configuration)": Это действие позволит вам создать новый маппер, который будет добавлять необходимые данные в токен.
4)Выберите "User Property": Этот тип маппера позволяет извлекать свойства пользователя из базы данных Keycloak.
5) Заполните поля и сохраните:
Эта настройка позволит вашему приложению получать имя пользователя в токене, что может быть полезно для аутентификации, авторизации и персонализации интерфейса пользователя.
Щя покажу, как это работает:
Для начала сгенерируем токен.
curl -X POST http://localhost:8080/realms/app_realm/protocol/openid-connect/token -H "Content-Type: application/x-www-form-urlencoded" -d "client_id=spring-app" -d "client_secret=ВАШ_КЛИЕНТСКИЙ_СЕКРЕТ" -d "username=test_user" -d "password=test123" -d "grant_type=password"
Скопируйте access_token из ответа.
Вставьте на https://jwt.io
Если все правильно, то вы должны увидеть "preferred_username": "test_user".
Подготовка базы данных
Создадим базу данных для хранения пользователей и их ролей. Схема включает три таблицы:
roles — хранит роли (USER, ADMIN, MODERATOR).
users — хранит информацию о пользователях (username, email).
user_roles — связывает пользователей и их роли.
SQL-запросы для создания базы данных
Конфигурация Spring Boot
В файле application.properties укажем параметры подключения к базе данных и Keycloak:
Обновление зависимостей в pom.xml:
Пишем код
Создание сущностей и репозитория
Сущность RoleEntity
Файл RoleEntity — это как список всех ролей в нашей компании. Например, есть роли "Администратор", "Пользователь", "Модератор". Эти роли — как ярлыки, которые помогают нам понять, что каждый человек может или не может делать. Когда пользователь регистрируется или входит в систему, система смотрит, какая у него роль, чтобы знать, что ему разрешено делать.
Сущность UserEntity
Файл UserEntity — это как список всех сотрудников. Каждый сотрудник — это отдельный пользователь в системе. У каждого пользователя есть ссылка на его роль через RoleId. Если у Анны RoleId = 1, значит, она — Администратор, потому что в таблице ролей под номером 1 написано "Администратор".
Репозиторий UserRepository
Это файл UserRepository посредник между кодом и базой данных. Когда системе нужно узнать что-то о пользователе, она обращается сюда.
Конфигурация безопасности KeycloakJwtAuthenticationConverter
Когда пользователь присылает токен (например, при входе или доступе к странице), этот файл "читает" токен и вытаскивает важную информацию, как имя пользователя и другие данные.
После того как токен разобран, информация из него передаётся другим частям программы, чтобы те знали, кто пользователь и что ему разрешено делать.
SecurityConfig
Это файл охранник у ворот. Он решает, кто может пройти в систему, а кто нет. Здесь прописаны все правила безопасности.
А ну конечно не забудем про TestController
Это испытатель системы. Этот файл нужен для проверки работы системы и её функций.
Как всё это работает вместе?
Когда пользователь входит в систему:
- Он присылает токен (как билет).
- KeycloakJwtAuthenticationConverter читает токен и передаёт данные системе.
Система проверяет пользователя:
- SecurityConfig определяет, может ли этот пользователь зайти на нужную страницу.
- Например, если это страница для Администраторов, файл проверяет, есть ли у пользователя роль Администратора.
Когда система работает с пользователями:
- Для добавления или получения данных о пользователе используется UserRepository.
- Например, чтобы узнать роль пользователя, система найдёт его через UserRepository и посмотрит, какой у него RoleId.
Ну а далее тесты, над пользователем test_user:
Вывод
Динамическое управление ролями через базу данных в интеграции с Keycloak обеспечивает гибкость и удобство. Вы можете легко обновлять роли пользователей и изменять их права доступа без необходимости обновлять токены. Такой подход подходит для масштабируемых приложений, где требуется централизованное управление правами.