От Hello World к Secure API: настраиваем Keycloak и Spring Security на Java

Салимжанов Р.Д.

Введение

В предыдущей статье мы подробно рассмотрели, что такое Keycloak и зачем его использовать для управления аутентификацией и авторизацией в приложениях. Мы также провели начальную настройку Keycloak, чтобы подготовить его к интеграции с нашими приложениями. Теперь пришло время перейти к более практическому аспекту и рассмотреть, как связка Keycloak и Spring Security может значительно упростить процесс аутентификации и авторизации в приложениях на базе Spring Boot.

Преимущества связки Keycloak + Spring Boot

Использование Keycloak в сочетании с Spring Boot предоставляет множество преимуществ:

  1. Упрощенная аутентификация: Keycloak позволяет легко настраивать различные методы аутентификации, включая OAuth2 и OpenID Connect, что делает процесс аутентификации более безопасным и гибким.
  2. Управление пользователями: Keycloak предоставляет мощные инструменты для управления пользователями, включая возможность создания ролей, групп и политик доступа, что упрощает администрирование.
  3. Интеграция с различными протоколами: Keycloak поддерживает множество протоколов аутентификации и авторизации, что позволяет легко интегрировать его с различными приложениями и сервисами.
  4. Масштабируемость: Использование Keycloak позволяет легко масштабировать ваше приложение, добавляя новые сервисы и обеспечивая единый механизм аутентификации.
  5. Безопасность: Keycloak обеспечивает высокий уровень безопасности благодаря поддержке современных стандартов и протоколов, а также возможности настройки многофакторной аутентификации.

В этой статье я предоставил готовый пример настройки аутентификации через Keycloak в приложении на Spring Boot. Вы увидите, как интегрировать Keycloak с Spring Security, настроить необходимые конфигурации и протестировать аутентификацию пользователей.

Подготовка окружения

В предыдущей статье мы подробно рассмотрели процесс установки Docker и настройки Keycloak с помощью docker-compose. Мы запустили Keycloak в контейнере, что позволило нам быстро развернуть сервер аутентификации без необходимости ручной установки. Теперь давайте кратко повторим основные шаги настройки Keycloak, которые нам понадобятся для интеграции с Spring Boot.

Вот как выглядит наш docker-compose.yml, который мы будем использовать для запуска Keycloak:

services: keycloak: image: quay.io/keycloak/keycloak:21.1.1 container_name: keycloak environment: - KEYCLOAK_ADMIN=admin - KEYCLOAK_ADMIN_PASSWORD=admin command: start-dev --http-port=8080 ports: - "8080:8080" networks: - keycloak-network networks: keycloak-network: driver: bridge

В этот раз мы сосредоточимся на базовой настройке без излишеств, без использования базы данных PostgreSQL и более сложных конфигураций, чтобы обеспечить простоту и доступность процесса.

Этот файл конфигурации создает контейнер с Keycloak, устанавливает учетные данные администратора и открывает порт 8080 для доступа к веб-интерфейсу Keycloak. После запуска контейнера вы сможете получить доступ к Keycloak по адресу http://localhost:8080 и продолжить настройку Realm, клиента и пользователей через веб-интерфейс.

Настройка Keycloak

Создание Realm

  1. Перейдите в веб-интерфейс Keycloak по адресу http://localhost:8080.
  2. Войдите с учетными данными администратора (admin/admin).
  3. На главной странице нажмите на кнопку " Create Realm".
  4. Введите имя (в моём случае test_realm) для нового Realm и нажмите "Create".

Создание Client

  1. Выберите созданный Realm в верхнем левом углу.
  2. Перейдите в раздел "Clients".
  3. Нажмите на кнопку "Create".
  4. Введите имя клиента (например, test_client).
  5. Нажмите "Save".
  6. В настройках клиента установите Client authentication ON, Authorization ON, Front channel logout ON, Backchannel logout session required ON, это база, далее настройку производите сами.
  7. Нажмите "Save".

Добавление пользователя и ролей

  1. Перейдите в раздел "Users".
  2. Нажмите на кнопку "Add User".
  3. Заполните необходимые поля (например, username, email) и нажмите "Save".
  4. Перейдите на вкладку "Credentials" и установите пароль для пользователя.
  5. Я создал testuser с паролем testuser
  6. Перейдите в раздел "Roles" и создайте необходимые роли, если они еще не созданы. (не обязательно, но желательно)
  7. Вернитесь к пользователю, выберите вкладку "Role Mappings" и назначьте созданные роли пользователю.

Создание Spring Boot приложения

Инициализация проекта

Перейдите на Spring Initializr (https://start.spring.io ) ну или инициализируйте через intellij idea.

Важные зависимости в pom.xml

Убедитесь, что в вашем pom.xml присутствуют следующие зависимости:

<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-resource-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>

Настройка application.properties

Создайте или отредактируйте файл src/main/resources/application.properties и добавьте следующие настройки:

server.port=8081 spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8080/realms/test_realm

Здесь test_realm — это имя вашего Realm, созданного в Keycloak.

Тестовая настройка приложения

На этом этапе мы создадим три файла, которые помогут настроить аутентификацию и авторизацию в нашем Spring Boot приложении с использованием Keycloak. Эти файлы включают:

  • TestController
  • SecurityConfig
  • KeycloakJwtAuthenticationConverter

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

На этом этапе ваш проект должен выглядеть примерно следующим образом:

От Hello World к Secure API: настраиваем Keycloak и Spring Security на Java

Теперь давайте подробнее рассмотрим каждый файл и его код.

1. TestController (REST-контроллер)

Назначение: Обработка HTTP-запросов и демонстрация работы аутентификации.

Что это такое?

Это контроллер с двумя примерами эндпоинтов:

  1. Публичный — доступен всем
  2. Защищенный — только для авторизованных

Как таблички на дверях:

  • 🟢 "Зал для всех" (/public/hello)
  • 🔒 "VIP-зал" (/hello)

Как это работает с SecurityConfig?

При запросе к /public/hello:

  • SecurityConfig видит правило permitAll()
  • Пропускает без проверки токена

При запросе к /hello:

  • SecurityConfig требует аутентификацию
  • Проверяет токен через Keycloak
  • Если всё ок — пускает в "VIP-зал"
package com.example.demo; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class TestController { // Публичный эндпоинт - доступен всем @GetMapping("/public/hello") public String publicHello() { return "Hello, this is a public endpoint!"; } // Защищенный эндпоинт - требует аутентификации @GetMapping("/hello") public String hello() { return "Hello, this is a secured endpoint!"; } }

Что важно:

@RestController – помечает класс как контроллер REST API

@GetMapping – определяет обработчик GET-запросов

Разделение на /public/** (публичные) и защищенные эндпоинты

2. SecurityConfig (Конфигурация безопасности)

Назначение: Настройка правил безопасности и интеграция с Keycloak.

это набор правил, который говорит Spring Security:

  • Какие URL защищать
  • Как проверять права доступа
  • Где брать информацию о пользователях

Представьте, что это инструкция для охранника на входе в клуб:

  • Кого пускать без проверки (публичные зоны)
  • Кого проверять по VIP-пропускам (токенам)
  • Какие права должны быть у посетителей

Как работает?

Правила доступа (authorizeHttpRequests): /public/** — публичная зона, вход без токена, Все остальные URL (anyRequest()) требуют аутентификации.

Интеграция с Keycloak (oauth2ResourceServer): Используем JWT-токены для проверки, Подключаем кастомный конвертер для "перевода" ролей.

package com.example.demo; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.web.SecurityFilterChain; @Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(authorize -> authorize .requestMatchers("/public/**").permitAll() // Разрешить без аутентификации .anyRequest().authenticated() // Все остальное требует аутентификации ) .oauth2ResourceServer(oauth2 -> oauth2 .jwt(jwt -> jwt .jwtAuthenticationConverter(new KeycloakJwtAuthenticationConverter()) ) ); return http.build(); } }

Ключевые элементы:

  • @EnableWebSecurity – активирует механизм безопасности Spring
  • .requestMatchers() – определяет правила доступа к URL
  • .oauth2ResourceServer() – подключает OAuth2-сервер (Keycloak)
  • .jwtAuthenticationConverter() – кастомная обработка JWT-токена

3. KeycloakJwtAuthenticationConverter (Конвертер JWT)

Назначение: Преобразование JWT-токена Keycloak в объект аутентификации Spring.

Зачем это нужно?

Представьте, что Keycloak отправляет вашему приложению JWT-токен, внутри которого есть список ролей пользователя. Но Spring Security "не понимает", где их искать, потому что Keycloak хранит роли в особом месте токена — в поле realm_access.roles.

Решение: Создаем конвертер, который:

· Берет стандартные права доступа (например, из scope).

· Добавляет роли из Keycloak.

· Переводит всё в формат, понятный Spring Security.

package com.example.demo; import org.springframework.core.convert.converter.Converter; import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.stream.Collectors; public class KeycloakJwtAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken> { @Override public AbstractAuthenticationToken convert(Jwt jwt) { // 1. Извлечение ролей из токена Map<String, Object> realmAccess = jwt.getClaim("realm_access"); Collection<String> roles = (Collection<String>) realmAccess.getOrDefault("roles", Collections.emptyList()); // 2. Преобразование ролей в GrantedAuthority Collection<GrantedAuthority> authorities = roles.stream() .map(role -> new SimpleGrantedAuthority("ROLE_" + role)) .collect(Collectors.toList()); // 3. Создание объекта аутентификации return new JwtAuthenticationToken(jwt, authorities); } }

Как всё это работает вместе?

  1. Пользователь делает запрос
  2. SecurityConfig проверяет:
  3. Если URL в публичной зоне — пропускает сразуЕсли защищенный — требует токен
  4. KeycloakJwtAuthenticationConverter "переводит" роли из токена
  5. TestController возвращает ответ в зависимости от проверок

Итоговая аналогия

Представьте приложение как здание:

  • SecurityConfig — книжка с правилами для охраны
  • TestController — комнаты с разным уровнем доступа
  • Keycloak — система выдачи пропусков
  • Токен — электронный пропуск с чипом (JWT)

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

Теперь, когда мы настроили наше Spring Boot приложение и интегрировали его с Keycloak, давайте протестируем его функциональность. Мы проверим публичный эндпоинт, получим токен через Keycloak и протестируем защищенный эндпоинт с использованием этого токена. Также рассмотрим частые ошибки, которые могут возникнуть в процессе.

Перед тем как протестировать наше Spring Boot приложение, необходимо убедиться, что все компоненты правильно запущены. Сначала запушен докер а потом Spring Boot приложение.

Теперь, когда оба компонента запущены, вы можете протестировать их функциональность.

Например, в браузере проверим публичный эндпоинт и приватный:

Как мы видим публичный доступен, а приватный нет.

Для доступа к приватному эндпоинту нам необходимо использовать токен доступа, полученный через Keycloak. Однако браузер не поддерживает прямую передачу токена в заголовках, поэтому мы это сделаем через командную строку:

От Hello World к Secure API: настраиваем Keycloak и Spring Security на Java

Для получения токена доступа через Keycloak вы можете использовать curl-запрос.

curl -X POST http://localhost:8080/realms/test_realm/protocol/openid-connect/token -H "Content-Type: application/x-www-form-urlencoded" -d "client_id=test_client&client_secret=bR4RDvBu8FGS4WdsgfdfgfTzEFS2n&username=testuser&password=testuser&grant_type=password"

client_secret=bR4RDvBu8FGS4Wb5Om9PiiOeKazEFS2n — секрет клиента, который также был сгенерирован в Keycloak. Чтобы узнать секрет клиента, Перейдите в раздел "Clients". Найдите и щелкните на имя клиента, для которого вы хотите узнать секрет (например, test_client). Перейдите на вкладку "Credentials" (Учетные данные). Здесь вы увидите поле "Secret" (Секрет), где будет указан ваш секрет клиента.

username=testuser — имя пользователя, для которого вы хотите получить токен.

password=testuser — пароль пользователя.

После выполнения этого запроса вы должны получить JSON-объект, содержащий access_token, который будет выглядеть примерно так:

От Hello World к Secure API: настраиваем Keycloak и Spring Security на Java

Используйте полученный токен для доступа к защищенному эндпоинту:

curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" http://localhost:8081/hello

От Hello World к Secure API: настраиваем Keycloak и Spring Security на Java

Вы должны получить ответ:

Hello, this is a secure endpoint!

Заключение

В этой статье мы успешно настроили защиту API для нашего Spring Boot приложения с помощью Keycloak. Вот основные итоги:

  • Мы настроили аутентификацию через Keycloak. Публичные эндпоинты (/public/**) доступны всем, а защищенные эндпоинты требуют JWT-токен для доступа.
  • Роли пользователей из Keycloak автоматически преобразуются в права доступа Spring Security, что упрощает управление доступом.
  • Keycloak запускается в Docker-контейнере, что делает развертывание и настройку более простыми и удобными.
  • Мы создали собственный конвертер ролей, который работает с данными Keycloak и совместим со Spring Security.

Таким образом, мы сделали простейшее тестовое приложение используя Keycloak с Spring Security.

СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ

1) Keycloak Documentation. // [электронный ресурс]. URL: https://www.keycloak.org/documentation (дата обращения 22.01.2025).

2) Интеграция Keycloak в приложение Spring Boot 3 с использованием протокола OAuth2.0 // [электронный ресурс]. URL: https://habr.com/ru/companies/axenix/articles/780422/ (дата обращения 23.01.2025).

3) Безопасность REST API от А до ПИ // [электронный ресурс]. URL: https://habr.com/ru/articles/503284/ (дата обращения 23.01.2025).

4) deepseek.com // [электронный ресурс]. URL: https://chat.deepseek.com/a/chat/s/1e3767a7-4dec-4ce6-9a5f-d1d19b85351f (дата обращения 23.01.2025).

4) spring // [электронный ресурс]. URL: https://start.spring.io (дата обращения 23.01.2025).

1
Начать дискуссию