
Хочешь облегчить себе жизнь и сэкономить время?
Привет! Если ты так же как и я решил использовать keycloak для аутентификации и авторизации в своей микро‑сервисной архитектуре, то я расскажу вам как правильно настроить сам keycloak, его рабочую среду а в конце мы подключим Active Directory к нашему приложению. Перед прочтением данного гайда прошу ознакомиться с keycloak по данной ссылке.
Стек Технологии
Я Джавист от кожи до костей и именно поэтому все микро‑сервисы у нас будут написаны в связке Java 18 и Spring Boot. Сборник проекта у меня Maven, так как довольно простой в своем применении.
Информация по другим сервисам:
PostGreSql — база данных;
PgAdmin — GUI БД;
Сервис Аутентификации — KeyCloak V19;
Zipkin + Sleuth — технология трассировки запросов;
Kafka — Брокер Сообщений;
Eureka Server — регистрация наших микро‑сервисов.
Готовый docker‑compose файл:
services: postgres: container_name: postgres-gilgamesh image: postgres environment: POSTGRES_USER: "здесь ваш username" POSTGRES_PASSWORD: "здесь ваш password" POSTGRES_HOST_AUTH_METHOD: trust POSTGRES_DB: keycloak_db volumes: - postgres:/data/postgres ports: - "5432:5432" networks: - postgres restart: unless-stopped keycloak: image: quay.io/keycloak/keycloak:legacy platform: linux/arm64 environment: DB_VENDOR: POSTGRES DB_ADDR: postgres DB_SCHEMA: public DB_DATABASE: keycloak_db DB_USER: "здесь ваш username от БД" DB_PASSWORD: "здесь ваш password от БД" KEYCLOAK_USER: "здесь ваш username" KEYCLOAK_HOSTNAME: localhost KEYCLOAK_PASSWORD: "здесь ваш пароль" ports: - 8082:8080 depends_on: - postgres networks: - postgres #Образ Готового LDAP если у вас нет тестового варианта ldap: image: rroemhild/test-openldap ports: - 10389:10389 - 10636:10636 networks: - postgres pgadmin: container_name: pgadmin-gilgamesh image: dpage/pgadmin4 environment: PGADMIN_DEFAULT_EMAIL: ${PGADMIN_DEFAULT_EMAIL:-pgadmin4@pgadmin.org} PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD:-admin} PGADMIN_CONFIG_SERVER_MODE: 'False' volumes: - pgadmin:/var/lib/pgadmin ports: - "5050:80" networks: - postgres restart: unless-stopped zipkin: image: openzipkin/zipkin container_name: zipkin-gilgamesh ports: - "9411:9411" networks: - spring zookeeper: image: confluentinc/cp-zookeeper:latest environment: ZOOKEEPER_CLIENT_PORT: 2181 ZOOKEEPER_TICK_TIME: 2000 ports: - 22181:2181 kafka: image: confluentinc/cp-kafka:latest depends_on: - zookeeper ports: - 29092:29092 environment: KAFKA_BROKER_ID: 1 KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092,PLAINTEXT_HOST://localhost:29092 KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 networks: postgres: driver: bridge spring: driver: bridge volumes: postgres: pgadmin:
Настройка и конфигурация нашего parent pom.xml
Данный проект будет работать локально и все микро‑сервисы будут находиться в одной директории. Благодаря этому мы можем использовать особенность maven'a в которой мы можем указать parent файл, где будут лежать наши основные зависимости, которые нам не нужно будет прописывать по несколько раз.
В моем проекте будет несколько модулей, давайте сразу же их укажем в нашем parent pom.xml файле:
<modules> <module>eureka-server</module> <module>incidents</module> <module>apigw</module> <module>clients</module> <module>kafka</module> <module>authentication</module> </modules>
У вас эти модули могут отличаться от моих, так как у вас может быть совершенно другая архитектура с другими сервисами.
Благодаря dependencyManagement в maven мы можем добавлять родительские зависимости в наш проект, благодаря которым, мы сможем использовать весь функционал его наследников. Выглядит это примерно вот так:
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>2.6.7</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.6.7</version> <scope>import</scope> <type>pom</type> </dependency> <dependency> <groupId>org.keycloak.bom</groupId> <artifactId>keycloak-adapter-bom</artifactId> <version>20.0.3</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
"Дополнительно" Автоматическое обертывание сервиса в докер
Для автоматическое обертывание образов в докер я использую мой любимый JIB.
Jib создает оптимизированные образы Docker и OCI для ваших Java‑приложений без использования демона Docker — и без глубокого освоения лучших практик Docker. Он доступен в виде плагинов для Maven и Gradle, а также в виде библиотеки Java.
Основные цели JIB
Быстрота — Быстрое развертывание изменений. Jib разделяет ваше приложение на несколько слоев, отделяя зависимости от классов. Теперь вам не нужно ждать, пока Docker пересоздаст все ваше Java‑приложение — достаточно развернуть только те слои, которые изменились.
Воспроизводимость — при пересборке образа контейнера с тем же содержимым всегда создается один и тот же образ. Никогда больше не вызывайте ненужных обновлений.
Без демона — Сократите количество зависимостей от CLI. Создайте свой образ Docker из Maven или Gradle и отправьте его в любой реестр по вашему выбору. Больше не нужно писать Docker‑файлы и вызывать docker build/push.
Более подробно о JIB можно почитать вот здесь: https://habr.com/ru/post/552 494/. В моем случае конфигурация выглядит вот так:
<build> <pluginManagement> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.6.7</version> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>com.google.cloud.tools</groupId> <artifactId>jib-maven-plugin</artifactId> <version>3.3.0</version> <configuration> <from> <image>eclipse-temurin:17</image> <platforms> <platform> <architecture>arm64</architecture> <os>linux</os> </platform> <platform> <architecture>amd64</architecture> <os>linux</os> </platform> </platforms> </from> <to> <tags> <tag>latest</tag> </tags> </to> </configuration> <executions> <execution> <phase>package</phase> <goals> <goal> build </goal> </goals> </execution> </executions> </plugin> </plugins> </pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>17</source> <target>17</target> </configuration> </plugin> </plugins> </build>
Настройка простого сервиса
Давайте создадим простой сервис под названием Incident и добавим в него несколько роутов и засекюрим их при помощи keycloak.
pom.xml:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>gilgamesh_project</artifactId> <groupId>com.project</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <packaging>jar</packaging> <artifactId>incidents</artifactId> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> <profiles> <profile> <id>build-docker-image</id> <build> <plugins> <plugin> <groupId>com.google.cloud.tools</groupId> <artifactId>jib-maven-plugin</artifactId> </plugin> </plugins> </build> </profile> </profiles> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-sleuth-zipkin</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <dependency> <groupId>org.springframework.kafka</groupId> <artifactId>spring-kafka</artifactId> </dependency> <dependency> <groupId>org.springframework.kafka</groupId> <artifactId>spring-kafka-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.project</groupId> <artifactId>kafka</artifactId> <version>1.0-SNAPSHOT</version> <scope>compile</scope> </dependency> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>4.0.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.9.0</version> </dependency> <dependency> <groupId>org.keycloak</groupId> <artifactId>keycloak-spring-boot-starter</artifactId> </dependency> </dependencies> </project>
application.yml файл:
server: port: 8080 spring: application: name: incidents datasource: password: password url: jdbc:postgresql://localhost:5432/incident-service username: postgres jpa: hibernate: ddl-auto: update properties: hibernate: dialect: org.hibernate.dialect.PostgreSQLDialect format_sql: true show-sql: true zipkin: base-url: http://localhost:9411 kafka: bootstrap-servers: localhost:29092 eureka: client: service-url: defaultZone: http://localhost:8761/eureka fetch-registry: true register-with-eureka: true
Чтобы не засорять данный пост давайте предположим, что мы уже подключили брокер сообщении и у нас есть сервис, который обращается к другому сервису для получения информации о какой-либо сущности. Дальше мы расписываем наш тестовый контроллер:
@RestController @RequestMapping("api/v1/incident/service") @RequiredArgsConstructor public class IncidentController { private final IncidentService incidentService; @PostMapping("/create") public ResponseEntity<IncidentCreateDtoResponse> create(@Valid @RequestBody IncidentDtoRequest incidentDtoRequest) { IncidentCreateDtoResponse incidentDtoResponse = IncidentCreateMapper.incidentCreateToDto(incidentService.create(incidentDtoRequest),new ArrayList<>()); return new ResponseEntity<>(incidentDtoResponse, HttpStatus.OK); } }
Замечательно, мы настроили наш сервис, теперь давайте подключим к нему keycloak.
Настройка и запуск KeyCloak
Откройте url по который вы указали в переменных keycloak в docker‑compose. Вас должна встретить следующая картина:

Если вы увидели данное окно, то поздравляю, вы успешно запустили keycloak. Теперь нажмите на admin console и авторизируйтесь в систему под данными, которые вы вводили в docker compose файл.
Дальше делайте все по этому алгоритму действий:
Создайте новый realm для вашего проекта.

Создайте новый client для вашего нового realm.

Создайте несколько тестовых пользователей и навесьте на них несколько тестовых ролей. В моем случае это пользователи:
username: arnur, roles: ["USER"]
username: adal, roles: ["USER"]
Теперь добавьте credentials вашего keycloak в ваш application.yml, чтобы в дальнейшем наше Spring Boot приложение могло генерировать токены и авторизировать пользователя:
keycloak: auth-server-url: http://localhost:"ваш порт"/auth resource: "название вашего клиента" bearer-only: true public-client: true realm: "название вашего реалма"
Теперь наступает время настройки вашего spring security. Для этого при создании вашей конфигурации вы будете наследоваться от класса KeycloakWebSecurityConfigurerAdapter. Данный класс позволяет нам создавать сессии и конфигурировать защиту наших роутов. Пример кода:
@KeycloakConfiguration @Import(KeycloakSpringBootConfigResolver.class) public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter { /** * Registers the KeycloakAuthenticationProvider with the authentication manager. */ @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { KeycloakAuthenticationProvider keycloakAuthenticationProvider = new KeycloakAuthenticationProvider(); keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper()); auth.authenticationProvider(keycloakAuthenticationProvider); } /** * Defines the session authentication strategy. */ @Bean @Override protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { return new RegisterSessionAuthenticationStrategy(buildSessionRegistry()); } @Bean protected SessionRegistry buildSessionRegistry() { return new SessionRegistryImpl(); } @Override protected void configure(HttpSecurity http) throws Exception { super.configure(http); http.cors().and().csrf().disable(); http.authorizeRequests() .antMatchers("/api/v1/incident/service/**").hasRole("USER"); http.authorizeRequests().anyRequest().permitAll(); } }
Поздравляю теперь вы можете проходить авторизацию при помощи oauth2.0 токена, через keycloak. Чтобы проверить это используйте postman и выберите авторизацию через токен. Вот самая дефолтная настройка:
Access Token URL: http://»ваш урл»/auth/realms/»ваш реалм»/protocol/openid‑connect/token
Client ID: наименование вашего клиента
grant type: password credentials
username: username пользователя
password: пароль пользователя
scope: openid
Подключение LDAP к вашему проекту
Теперь начинается самый сок! KeyCloak позволяет нам использовать пользователей из AD для авторизации. Для примера я буду использовать готовый образ LDAP, который вы можете скачать по этой ссылке: https://hub.docker.com/r/rroemhild/test-openldap/. Делайте пул и разворачивайте в докере, мы живем в 21ом веке!
Чтобы добавить LDAP к вашему Keyсloak зайдите в раздел "User Federation" и выберите LDAP как ваш "User Provider" и укажите все согласно инструкции:
Console display name: какой душе угодно;
Connection URL: тут если вы развернули ldap через докер url будет такой: ldap://»ip вашего компа»:10 389. Почему‑то keycloak напрочь отказывается видеть ваш ldap через localhost даже если вы внедрите их в один docker‑network;
Bind Type: указывает BN вашего админа в случае тестового LDAP это: cn=admin,dc=planetexpress,dc=com
Bind credentials: указывает пароль вашего админа в случае тестового LDAP это: GoodNewsEveryone
EDIT MODE: советую ставить READ_ONLY
Users DN: указывает общую инфу вашего рядового пользователя в случае тестового LDAP это: ou=people,dc=planetexpress,dc=com
Username LDAP attribute: Имя атрибута LDAP, который отображается как имя пользователя Keycloak. Для нашего тестового LDAP это: uid
RDN LDAP attribute: Имя атрибута LDAP, который используется в качестве RDN (верхнего атрибута) типичного DN пользователя. Для нашего тестового LDAP это: uid
UUID LDAP attribute: Имя атрибута LDAP, который используется в качестве уникального идентификатора объекта (UUID) для объектов в LDAP. Для нашего тестового LDAP это: entryUUID
User object classes: Все значения атрибута LDAP objectClass для пользователей в LDAP, разделенные запятыми. Для нашего тестового LDAP это: inetOrgPerson, organizationalPerson, person, top
После этого нажмите save и в action выберите sync all users. После этого все юзеры из ldap должны синхронизироваться с вашим keycloak клиентом.
Дальше зайдите во вкладку Users и вы должны наблюдать примерно такую картину:

Теперь попробуйте задать некоторым пользователям созданную вами до этого роль (в моем случае это USER). И попробуйте сгенерировать токен. Если у вас все получилось, то поздравляю, вы успешно настроили KeyCloak как сервис аутентификации.
Итоги
Keycloak супер универсальный сервис авторизации и аутентификации. Он сильно экономит время разработки и позволяет настраивать систему авторизации как душе угодно. Keycloak так же крайне просто конфигурируется и запускается на любоей системе, а так же имеет открытый исходный код. А самое главное, он написан на JAVA!
Всем добра и позитива! Пользуйтесь Kcell и Activ!