В современном мире веб-разработки обеспечение безопасности пользовательских идентификаторов и управление доступом к ресурсам становятся все более важными задачами. Один из мощных инструментов, предоставляющих полноценное решение для этих задач, это Keycloak, современная система управления идентичностью и доступом.
Keycloak - это средство с открытым исходным кодом, предоставляющее полнофункциональную платформу для управления идентичностью и доступом. Он поддерживает различные стандарты безопасности, включая OAuth 2.0, OpenID Connect и другие, что делает его идеальным выбором для современных веб-приложений.
В данной статье мы рассмотрим процесс интеграции Keycloak в наше приложение Spring Boot 3 в качестве сервера авторизации с использованием протокола OAuth2. Обсудим смысл OAuth2, его механизм работы и сравним его с другими протоколами. Кроме того, мы настроим Keycloak с использованием Docker Compose, воспользовавшись PostgreSQL в качестве базы данных для Keycloak. Затем мы интегрируем Keycloak с нашим приложением Spring Boot 3, используя протокол OAuth2. Также мы подключим Keycloak Admin Client и, наконец, проверим функциональность всей системы.

OAuth 2.0: Механизм работы OAuth2 и его структура
OAuth2 является протоколом авторизации, предназначенным для делегирования доступа к ресурсам от имени пользователя без передачи ему его учетных данных. Протокол поддерживает различные потоки (grant types), включая Authorization Code, Implicit, Resource Owner Password Credentials и Client Credentials, что делает его гибким для различных сценариев использования. Основная идея OAuth2 заключается в том, чтобы позволить пользователям предоставлять доступ к своим данным на сторонних ресурсах, не раскрывая свои учетные данные. Вместо этого, для аутентификации используются токены, которые выдаются после успешной авторизации. Такими токенами являются Access Token (Токен доступа) и Refresh Token (Токен обновления). Access токен предоставляет приложению временный доступ к ресурсам на сервере. Он имеет ограниченный срок действия, чаще всего короткий (несколько минут или часов). Access токен передается при каждом запросе к защищенному ресурсу. Refresh токен используется для обновления Access токена после его истечения срока действия. У Refresh токена обычно более длительный срок действия, чем у Access токена. Он предназначен для длительного использования. Когда Access токен истекает, приложение отправляет Refresh токен на сервер авторизации и сервер возвращает новый Access токен. В контексте OAuth2 существуют специальные серверы авторизации, также известные как Authorization Servers. Эти серверы играют ключевую роль в процессе выдачи токенов и управлении доступом. Различные веб-сервисы, такие как Google, Facebook, GitHub и другие, являются примерами таких серверов. Основными компонентами механизма OAuth 2 являются:
Resource Owner (Владелец ресурса): Пользователь, владеющий данными (ресурсами), к которым запрашивается доступ.
Client (Клиент): Приложение или сервис, запрашивающий доступ к ресурсам у владельца.
Authorization Server (Сервер авторизации): Сервер, управляющий процессом авторизации и выдачей токенов.
Resource Server (Сервер ресурсов): Сервер, управляющий защищенными ресурсами, к которым запрашивается доступ.
Механизм работы OAuth2

1. Регистрация приложения (Client Registration):
Клиент (ваше веб-приложение или сервис) должен быть предварительно зарегистрирован на сервере авторизации. В этот момент вы получаете идентификатор клиента (Client ID) и секрет (Client Secret), которые используются для аутентификации вашего приложения.
2. Для получения доступ к защищенному ресурсу веб-приложение направляет пользователя на страницу авторизации Keycloak.
3. Подтверждение авторизации (User Authentication):
Пользователь вводит свои учетные данные на странице сервера авторизации и подтверждает предоставление доступа вашему приложению.
4․ Получение кода авторизации (Authorization Code):
После успешной аутентификации сервер авторизации направляет пользователя обратно на ваш сайт по указанному вами URL-перенаправления, при этом включается код авторизации.
5․ Обмен кода на токены (Token Exchange):
Клиент использует полученный код для запроса Access Token и, при необходимости, Refresh Token у сервера авторизации. Запрос выглядит примерно так:
6․ Выдача токенов (Token Issuance):
После успешной проверки кода сервер авторизации отвечает JSON-объектом, который содержит Access Token и, возможно, Refresh Token.
7. Доступ к ресурсам (Access Resources):
Клиент использует полученный Access Token для доступа к защищенным ресурсам, отправляя его в заголовке запроса.
8․ Обновление токена (Token Refresh):
При истечении срока действия Access Token клиент может использовать Refresh Token для получения нового Access Token без повторной аутентификации владельца ресурса.
Сравнение Oauth2 с другими подходами авторизации
Критерий | OAuth 2.0 | OpenID Connect | SAML | Basic |
Передача пароля | Не рекомендуется, используются маркеры доступа (Access tokens)
| Может предоставлять аутентификацию, включая передачу пароля | Используется для передачи атрибутов, включая аутентификацию | Основной метод, небезопасен при передаче через сеть |
Передача ролей | Возможность передачи различных разрешений. Например, чтение данных, запись данных, выполнение определенных действий и т. д. | Может передавать информацию о ролях через атрибуты | Специализируется на передаче атрибутов, включая роли | Ограничен в передаче дополнительной информации о ролях |
Простота реализации | Относительно простая реализация | Может быть более сложным из-за дополнительных функциональных возможностей | Может быть более сложным в сравнении с OAuth 2.0 | Прост в реализации, но ограничен функциональностью |
Поддержка устройств | Поддерживает различные сценарии, включая мобильные устройства и веб-приложения | Поддерживает различные сценарии, но может быть более сложным в реализации | Традиционно ориентирован на веб-приложения | Ограничен в поддержке современных сценариев использования |
Расширяемость | Позволяет расширять функциональность через дополнительные спецификации и профили | Предоставляет расширенные возможности для удовлетворения специфических требований | Предоставляет возможность определения дополнительных профилей и расширений | Ограничен в расширяемости и возможностях |
Oauth2 и Keykloack
В нашем случае, для обеспечения функциональности сервера авторизации, мы используем Keycloak. Keycloak предоставляет не только механизм выдачи токенов, но и обширные возможности управления идентичностью, включая аутентификацию, авторизацию и управление пользователями. Таким образом, при использовании Keycloak с OAuth2 ваше веб-приложение может делегировать процесс аутентификации и управление доступом к Keycloak, который выступает в роли вашего собственного сервера авторизации. Это обеспечивает высокий уровень безопасности и управляемости в процессе работы с данными пользователей, при этом не требуя передачи конфиденциальных учетных данных вашему веб-приложению.
Установка и настройка Keycloak
Начнем с установки Keycloak. Вы можете скачать его с его официального сайта https://www.keycloak.org/downloads.html и следовать инструкциям по установке для вашей операционной системы. Для удобства установки и развертывания, мы будем использовать будем использовать Docker Compose файл и последнюю версию докер образа Keycloak, которая на данный момент является 16.1.1. В зависимости от выбранной версии Keycloak могут отличаться пользовательский интерфейс и некоторые другие элементы.
Создайте файл docker-compose.yml в вашем проекте и добавьте следующий пример контента файла:
version: '3' services: postgres: image: postgres volumes: - postgres_data:/var/lib/postgresql/data environment: POSTGRES_DB: keycloakdb POSTGRES_USER: keycloakuser POSTGRES_PASSWORD: keycloakpass networks: - keycloak-network keycloak: image: jboss/keycloak:16.1.1 ports: - 8180:8080 environment: DB_VENDOR: POSTGRES DB_ADDR: postgres DB_DATABASE: keycloakdb DB_SCHEMA: public DB_USER: keycloakuser DB_PASSWORD: keycloakpass KEYCLOAK_USER: admin KEYCLOAK_PASSWORD: adminpass networks: - keycloak-network depends_on: - postgres networks: keycloak-network: driver: bridge volumes: postgres_data: driver: local
Keycloak по умолчанию использует встроенную базу данных H2. Однако для production развертывания рекомендуется использовать более надежную и масштабируемую базу данных, такую как PostgreSQL, MySQL или MariaDB. В предоставленном файле Docker Compose конфигурация настроена на использование PostgreSQL в качестве базы данных для Keycloak.
Запустите команду docker-compose up из директории проекта, где находится файл docker-compose.yml. После выполнения этой команды, сервер Keycloak начнет работу на порту 8180. Для доступа к административной консоли перейдите по следующей ссылке: http://localhost:8180/.
После этого введите администраторский логин и пароль, указанные в файле docker-compose.yml (admin, adminpass).
Создаем Realm:
"Realm" в Keycloak - это административная единица (контейнер), которая объединяет набор клиентов (applications), пользователей и настроек безопасности в рамках одной области. Каждое приложение в Keycloak принадлежит определенному realm, и realm предоставляет изолированное пространство для управления пользователями, аутентификации и авторизации.
После успешного входа, перейдите к следующему шагу и создайте новый realm, нажав "Add realm".

Укажите имя и нажмите "Create".

Клиент представляет собой ваше веб-приложение, которое будет взаимодействовать с Keycloak для аутентификации.
Перейдите к следующему шагу и внутри вашего Realm создайте клиента. В левом боковом меню перейдите в раздел "Clients" и нажмите "Create" в правом верхнем углу.
Создайте клиента, указав Client ID, и нажмите "Save".

Перейдём к настройке клиента.
Установите тип доступа (Access Type) на "confidential", отключите "Direct Access Grants Enabled", включите "Service Accounts Enabled", укажите допустимые перенаправления (Valid redirect URIs) как http://localhost:8080/* и нажмите "Save".


Перейдите к "Client Scopes" -> "Roles" -> "Mappers" -> "Realm Roles" и включите "Add to userinfo", затем нажмите "Save".

Добавим роли для клиента:
1. Перейдите в раздел "Clients" и выберите конкретного клиента (например, "myclient").
2. Внутри клиента перейдите в "Service Account Roles" -> "Client Roles".
3. Нажмите на "Select a Client" и выберите "realm-management" из списка
4. В разделе "Available Roles" найдите роли "manage-users", "query-users", "view-users", "view-realm". Нажмите на каждую из этих ролей, чтобы выделить их. Нажмите "Add Selected", чтобы добавить выбранные роли к служебной учетной записи вашего клиента.
Эти настройки гарантируют, что служебная учетная запись вашего клиента будет иметь соответствующие права доступа к управлению пользователями, запросу данных, просмотру пользователей.

С помощью импорта разработчики могут избежать необходимости повторного ввода одних и тех же настроек безопасности. Это снижает риск возникновения ошибок из-за человеческого фактора и ускоряет процесс разработки.
Для экспорта реалма необходимо перейти в раздел "Export" в левом боковом меню, включить опции "Export groups and roles" и "Export clients", затем нажать кнопку "Export".

Экспортированный из пользовательского интерфейса Keycloak файл не включает информацию о пользователях и секретах клиента. Однако существует метод добавления секрета в файл экспорта realm в формате JSON. Откройте его для редактирования.
Для добавления секрета клиента найдите клиента с секретом, который нужно сохранить (например, поиск по "clientId": "myclient"), затем найдите "secret": "**********" внутри него и замените звездочки на секрет клиента, который нужно сохранить.
После этого добавьте экспортированный файл в приложение и добавьте следующие изменения в docker-compose.yml файле
keycloak: KEYCLOAK_IMPORT: /opt/jboss/keycloak/standalone/configuration/realm-export.json volumes: - ./realm-export.json:/opt/jboss/keycloak/standalone/configuration/realm-export.json
Запустите команду docker-compose up из директории проекта, где находится файл docker-compose.yml.
Как создать пользователя:
Перейдите в раздел "Users".
Нажмите на "Add user", укажите желаемое имя пользователя (например, testuser) и нажмите "Save".

Перейдите в "Users" -> "testuser" -> "Credentials".
Укажите пароль, повторите пароль и нажмите "Set Password".
Опционально, можно отключить опцию "Temporary", чтобы при первом входе пользователь был обязан изменить пароль.

Как создать роли:
Перейдите в раздел "Roles".
Нажмите на "Add role", укажите название роли (например, admin) и нажмите "Save".

Как назначить роль пользователю:
Перейдите в раздел "Users".
Выберите нужного пользователя, например, "testuser".
Перейдите в "Role Mappings" -> "Realm roles" -> "Available".
Нажмите на нужную роль, например, "admin", и затем "Add Selected".

Группы. Как их создать
1. Организация Пользователей:
В Keycloak группы используются для организации пользователей. Вы можете создавать группы и добавлять пользователей. Пользователи наследуют атрибуты и отображения ролей, присвоенные каждой группе. Группы иерархичны. Группа может иметь много подгрупп, но у группы может быть только один родитель. Подгруппы наследуют атрибуты и отображения ролей от родительской группы. Таким образом, если у вас есть родительская группа и дочерняя группа, и пользователь принадлежит только к дочерней группе, то пользователь наследует атрибуты и отображения ролей как от родительской, так и от дочерней группы.
2. Управление Доступом:
Группы предоставляют удобный способ управления правами доступа. Например, если у вас есть приложение с разными разделами или функциональностью, вы можете создать группы, представляющие эти разделы, и назначить пользователям соответствующие группы для управления их доступом.
3. Роли в Группах:
В Keycloak группы могут также содержать роли, которые определяют права доступа. Например, у вас может быть группа "Администраторы" с ролью "Администратор", что позволяет пользователям в этой группе иметь особые привилегии.
Для создания групп перейдите на вкладку "Groups", затем нажмите "New", укажите название группы и нажмите "Save".

Для добавления роли к группе перейдите на вкладку Role Mappings внутри группы, из Available roles выберите нужную роль, например, "admin", и затем "Add Selected".

Для добавления пользователя в группу перейдите на вкладку "Users", выберите нужного пользователя, затем перейдите на вкладку "Groups". Внутри профиля пользователя в разделе "Available groups" выберите нужную группу и нажмите "Join".

Как добавить дополнительные атрибуты в Access токен:
Access token содержит некоторые метаданные, но мы можем добавить в него атрибуты. Давайте посмотрим, как добавить пользовательский атрибут в токен. Перейдите на вкладку Users, выберите пользователя, затем откройте вкладку Attributes. Нажмите кнопку Add, добавьте ключ для атрибута (например, customAttribute) и значение (например, customAttributeValue), затем нажмите Save.

После этого нам нужно добавить маппер для этого атрибута. Чтобы сделать это, перейдите в раздел Clients, выберите свой клиент, затем перейдите на вкладку Mappers и нажмите Create. Укажите имя маппера (например, customMapper), установите тип маппера как User Attribute, выберите Claim JSON Type как string (вы можете выбрать другие типы в зависимости от ваших потребностей), укажите имя атрибута пользователя и имя атрибута токена как customAttribute, затем нажмите Save.

Теперь мы можем получать токен от Keycloak и там видеть атрибут, который мы добавили.

В этой части статьи мы установили и настроили Keycloak, подключили базу данных для использования Keycloak, создали Realm, создали и настроили клиента, экспортировали и импортировали Realm, создали пользователей, создали роли, назначили роли пользователям, создали группу, добавили роль к группе, добавили пользователей в группу и добавили дополнительный атрибут в Access token. Если вы хотите использовать другую конфигурацию, добавить дополнительные настройки или выполнить другие действия, вы можете найти соответствующую информацию на официальной странице документации Keycloak по следующей ссылке https://www.keycloak.org/documentation.

Настройка безопасности приложения Spring MVC
Теперь перейдем к конфигурации нашего приложения Spring Boot и добавим безопасность.
Добавьте следующие зависимости:
dependencies { // Другие зависимости вашего проекта // Зависимость для поддержки OAuth2-клиента в Spring Boot implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' // Зависимость для поддержки безопасности в Spring Boot implementation 'org.springframework.boot:spring-boot-starter-security' }
Теперь добавим конфигурационный класс для безопасности (security).
Тут большая страшилка
import static org.springframework.security.config.Customizer.withDefaults; import java.net.URI; import java.util.Collection; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; import org.springframework.security.core.session.SessionRegistryImpl; import org.springframework.security.oauth2.client.oidc.web.logout.OidcClientInitiatedLogoutSuccessHandler; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.core.oidc.OidcUserInfo; import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy; import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; @Configuration @EnableWebSecurity @EnableMethodSecurity( securedEnabled = true, jsr250Enabled = true ) public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http, OidcClientInitiatedLogoutSuccessHandler oidcLogoutSuccessHandler) throws Exception { return http.authorizeHttpRequests(authorise -> authorise .requestMatchers("/static/**") .permitAll() .anyRequest() .authenticated()) .csrf(AbstractHttpConfigurer::disable) .exceptionHandling(authorise -> authorise.accessDeniedPage("/access-denied")) .oauth2Login(withDefaults()) .logout(logout -> logout.logoutSuccessHandler(oidcLogoutSuccessHandler)).build(); } @Bean @SuppressWarnings("unchecked") public GrantedAuthoritiesMapper userAuthoritiesMapper() { return (authorities) -> { Set<GrantedAuthority> mappedAuthorities = new HashSet<>(); authorities.forEach(authority -> { if (authority instanceof OidcUserAuthority oidcUserAuthority) { OidcUserInfo userInfo = oidcUserAuthority.getUserInfo(); Map<String, Object> realmAccess = userInfo.getClaim("realm_access"); Collection<String> realmRoles; if (realmAccess != null && (realmRoles = (Collection<String>) realmAccess.get("roles")) != null) { realmRoles .forEach(role -> mappedAuthorities.add(new SimpleGrantedAuthority("ROLE_" + role))); } } }); return mappedAuthorities; }; } @Bean OidcClientInitiatedLogoutSuccessHandler oidcLogoutSuccessHandler(ClientRegistrationRepository clientRegistrationRepository) { OidcClientInitiatedLogoutSuccessHandler successHandler = new OidcClientInitiatedLogoutSuccessHandler(clientRegistrationRepository); successHandler.setPostLogoutRedirectUri(URI.create("http://localhost:8080").toString()); return successHandler; } @Bean protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl()); } }
В файле application.yml добавим следующие настройки
spring: security: oauth2: client: registration: keycloak: client-id: myrealm client-secret: chnageme scope: openid, profile authorization-grant-type: authorization_code provider: keycloak: issuer-uri: http://localhost:8180/auth/realms/myrealm user-name-attribute: preferred_username keycloak: server-url: http://localhost:8180/auth realm: myrealm username: admin password: adminpass
Замените значение changeme на уникальный секрет, который был сгенерирован для вашего клиента в Keycloak. Чтобы сгенерировать новый client-secret, перейдите в меню 'Clients'. В разделе 'Credentials' скопируйте существующий client-secret или, если его нет, нажмите 'Regenerate Secret', чтобы получить новое значение. Затем вставьте его в качестве значения для параметра client-secret в файле конфигурации вашего приложения (application.yml).
Отметим, что данная конфигурация предназначена для архитектуры MVC, когда наше приложение выступает в роли клиента и ресурсного сервера. Если ваше приложение является REST приложением и предназначено только для роли ресурсного сервера, вы можете найти соответствующую конфигурацию по следующим ссылкам:
https://github.com/Pask423/keycloak-springboot/tree/master/base-integration-spring-boot-3
https://www.youtube.com/watch?v=vmEWywGzWbA
Программное взаимодействие с Keycloak через Admin Client
Admin Client в Keycloak позволяет программно создавать пользователей и проводить разнообразные операции, обеспечивая автоматизацию административных задач без необходимости использования веб-консоли. Это удобство позволяет эффективно управлять идентификацией и доступом в системе.
Для использования админ клиента необходимо добавить следующий зависимость:
'org.keycloak:keycloak-admin-client:22.0.1'
Создай конфигурационый класс KeyCloakConfig, который вернет экземпляр Keycloak.
import org.keycloak.OAuth2Constants; import org.keycloak.admin.client.Keycloak; import org.keycloak.admin.client.KeycloakBuilder; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class KeycloakConfig { @Value("${keycloak.server-url}") private String serverUrl; @Value("${keycloak.realm}") private String realm; @Value("${spring.security.oauth2.client.registration.keycloak.client-id}") private String clientId; @Value("${spring.security.oauth2.client.registration.keycloak.client-secret}") private String clientSecret; @Value("${keycloak.username}") private String userName; @Value("${keycloak.password}") private String password; @Bean public Keycloak keycloak() { return KeycloakBuilder.builder() .serverUrl(serverUrl) .realm(realm) .grantType(OAuth2Constants.CLIENT_CREDENTIALS) .clientId(clientId) .clientSecret(clientSecret) .username(userName) .password(password) .build(); } }
Тестирование функционала
Для тестирования всего функционала создаем:
Простой Controller, DTO и Service
import jakarta.annotation.security.RolesAllowed; import java.security.Principal; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @Controller @RequiredArgsConstructor public class MyController { private final KeyCloakService keyCloakService; @GetMapping("/admin") @RolesAllowed("admin") public String admin(Principal principal, Model model) { model.addAttribute("username", principal.getName()); return "admin"; } @GetMapping("/user") public String user(Principal principal, Model model) { model.addAttribute("username", principal.getName()); return "user"; } @GetMapping("/create") public String createUser() { return "create-user"; } @PostMapping("/create") public String createUser(@RequestBody UserRequestDTO userRequestDTO) { keyCloakService.addUser(userRequestDTO); return "index"; } } import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.servlet.ModelAndView; @Controller public class ErrorController { @GetMapping("/access-denied") public ModelAndView showAccessDeniedPage() { return new ModelAndView("access-denied"); } } import lombok.Data; @Data public class UserRequestDTO { private String username; private String password; private String role; } import java.util.Collections; import java.util.List; import lombok.RequiredArgsConstructor; import org.keycloak.admin.client.Keycloak; import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.admin.client.resource.RoleMappingResource; import org.keycloak.admin.client.resource.UserResource; import org.keycloak.admin.client.resource.UsersResource; import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor public class KeyCloakService { private final Keycloak keycloak; @Value("${keycloak.realm}") private String realm; public void addUser(UserRequestDTO dto) { String username = dto.getUsername(); CredentialRepresentation credential = createPasswordCredentials(dto.getPassword()); UserRepresentation user = new UserRepresentation(); user.setUsername(username); user.setCredentials(Collections.singletonList(credential)); user.setEnabled(true); UsersResource usersResource = getUsersResource(); usersResource.create(user); addRealmRoleToUser(username, dto.getRole()); } private void addRealmRoleToUser(String userName, String roleName) { RealmResource realmResource = keycloak.realm(realm); List<UserRepresentation> users = realmResource.users().search(userName); UserResource userResource = realmResource.users().get(users.get(0).getId()); RoleRepresentation role = realmResource.roles().get(roleName).toRepresentation(); RoleMappingResource roleMappingResource = userResource.roles(); roleMappingResource.realmLevel().add(Collections.singletonList(role)); } private UsersResource getUsersResource() { return keycloak.realm(realm).users(); } private static CredentialRepresentation createPasswordCredentials(String password) { CredentialRepresentation passwordCredentials = new CredentialRepresentation(); passwordCredentials.setTemporary(false); passwordCredentials.setType(CredentialRepresentation.PASSWORD); passwordCredentials.setValue(password); return passwordCredentials; } }
Html страницы
index.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Keycloak Spring Boot Integration</title> </head> <body> <h1>Welcome to Keycloak Integration App</h1> <p><a th:href="@{/admin}">Admin Page</a></p> <p><a th:href="@{/user}">User Page</a></p> <p><a th:href="@{/create}">Create User</a></p> <p><a th:href="@{/logout}">Logout</a></p> </body> </html>
access-denied.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Нет доступа</title> </head> <body> <h1>You do not have permission to access this resource</h1> <a href="index.html" th:href="@{~/}"> <button>Return to home page</button> </a> </body> </html>
admin.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Admin Page</title> </head> <body> <h1>Welcome, <span th:text="${username}"></span>!</h1> <p>This is the Admin Page.</p> <p><a th:href="@{/}">Return to Home Page</a></p> <p><a th:href="@{/logout}">Logout</a></p> </body> </html>
user.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>User Page</title> </head> <body> <h1>Welcome, <span th:text="${username}"></span>!</h1> <p>This is the User Page.</p> <p><a th:href="@{/}">Return to Home Page</a></p> <p><a th:href="@{/logout}">Logout</a></p> </body> </html>
create-user.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Create user</title> </head> <body> <form id="userForm" th:action="@{/create}" method="post"> <label for="username">Username:</label> <input type="text" id="username" name="username" required> <br> <label for="password">Password:</label> <input type="password" id="password" name="password" required> <br> <label for="role">Role:</label> <input type="text" id="role" name="role" required> <br> <button type="button" onclick="submitForm()">Create User</button> </form> <script> function submitForm() { var form = document.getElementById('userForm'); var formData = { username: form.elements.username.value, password: form.elements.password.value, role: form.elements.role.value }; var jsonData = JSON.stringify(formData); fetch(form.action, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: jsonData }); } </script> </body> </html>
Создаем двух пользователей в Keycloak с разными ролями, например, "firstuser" с ролью "admin" и "seconduser" с ролью "user".
В браузере перейдите по адресу http://localhost:8180/. Вас перенаправит на страницу аутентификации Keycloak. Войдите в систему как пользователь "firstuser".

Будет открываться главная страница


Нажмите на Admin Page и доступ будет запрещен, так как пользователь "seconduser" не имеет роль "admin"

Теперь программатично создаем пользователя с ролю. Нажмите на Create User и будет открываться страница для создания пользователя. Заполните поля 'username', 'password' и 'role' (выбрав одну из ролей, доступных в Keycloak), затем нажмите 'Create user'.

Перейдите в админ-консоль Keycloak по адресу localhost:8180, затем в меню 'Users' нажмите 'View All Users', и вы увидите созданного пользователя (testuser) с ролью 'admin'.



Мы внедрили безопасность в наше приложение, проверили и убедились, что ограничения по ролям работают, еще мы можем программно взаимодействовать с Keycloak, например, создавать пользователей.
Заключение
Этот проект можно посмотреть на GitHub по следующей ссылке.
Несмотря на то, что Keycloak является мощным инструментом для управления идентификацией и доступом, у него есть некоторые недостатки:
1. Сложность настройки
Некоторые пользователи могут столкнуться с трудностями при настройке Keycloak из-за обширного набора функций и параметров, что может потребовать времени и усилий.
2. Сложность обновлений
Переход на новые версии Keycloak иногда может быть сложным процессом из-за изменений в API и структуре данных.
В заключении отметим, что мы успешно произвели интеграцию Keycloak с протоколом OAuth 2 в приложении Spring Boot 3. Таким образом, наш опыт успешной интеграции Keycloak и OAuth 2 в Spring Boot 3 подтверждает, что данное решение не только соответствует высоким стандартам безопасности, но также обеспечивает удобство в разработке и поддержке нашего веб-приложения.
Мы можем подчеркнуть несколько важных моментов, касающихся интеграции Keycloak с протоколом OAuth 2 в приложении Spring Boot 3.
Во-первых, стоит отметить, что протокол OAuth 2 доказал свою эффективность в обеспечении безопасности и авторизации. Сравнив его с другими протоколами, мы выявили его гибкость и простоту в реализации, что делает его привлекательным выбором для многих сценариев авторизации.
В контексте интеграции с Keycloak, использование Docker Compose для настройки среды и PostgreSQL в качестве базы данных приносит дополнительные выгоды. Это позволяет эффективно управлять конфигурацией Keycloak, а также обеспечивает легкость масштабирования и переносимость между средами.
Внедрение импорта для Realm Keycloak существенно повышает уровень автоматизации настройки, что в свою очередь значительно упрощает и ускоряет процессы внедрения.
Стоит особо выделить важность интеграции административного клиента Keycloak. Этот элемент играет решающую роль в программном управлении процессами, предоставляя возможность их реализации через код. Эта интеграция не только предоставляет более гибкий и мощный способ управления Keycloak, но также существенно снижает необходимость вручную взаимодействовать с веб-консолью.
В итоге, интеграция Keycloak с протоколом OAuth 2, настроенного с использованием Docker Compose, в сочетании с программной интеграцией административного клиента в приложение Spring Boot 3, обеспечивает не только высокий уровень безопасности, но также максимальную степень автоматизации в управлении процессами аутентификации и авторизации.
Какие технологии вы используете для аутентификации и авторизации в своем Spring Boot приложении?
