Как реализовать аутентификацию через Keycloak в Spring Security?

«Как реализовать аутентификацию через Keycloak в Spring Security?» — вопрос из категории Spring, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Интеграция Spring Security с Keycloak позволяет делегировать аутентификацию и авторизацию внешнему OAuth 2.0 / OpenID Connect (OIDC) провайдеру.

Базовая настройка:

  1. Добавление зависимостей (Maven):

    <!-- Стартер Keycloak для Spring Boot -->
    <dependency>
        <groupId>org.keycloak</groupId>
        <artifactId>keycloak-spring-boot-starter</artifactId>
    </dependency>
  2. Конфигурация в application.yml:

    keycloak:
      realm: my-application-realm
      auth-server-url: ${KEYCLOAK_URL:http://localhost:8080/auth} # URL сервера Keycloak
      resource: my-spring-backend-client # Client ID в Keycloak
      public-client: false # Для конфиденциальных клиентов (backend)
      credentials:
        secret: ${KEYCLOAK_CLIENT_SECRET} # Секрет клиента
      ssl-required: external
      use-resource-role-mappings: true
  3. Конфигурация безопасности (современный подход без устаревшего адаптера):

    import org.springframework.context.annotation.Bean;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
    import org.springframework.security.web.SecurityFilterChain;
    
    @EnableWebSecurity
    public class SecurityConfig {
    
        @Bean
        public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
            // Конвертер для преобразования ролей из JWT-токена Keycloak
            JwtAuthenticationConverter jwtConverter = new JwtAuthenticationConverter();
            jwtConverter.setJwtGrantedAuthoritiesConverter(new KeycloakJwtRolesConverter());
    
            http
                .authorizeHttpRequests(authz -> authz
                    .requestMatchers("/api/public/**").permitAll()
                    .requestMatchers("/api/admin/**").hasRole("ADMIN") // Роли из Keycloak
                    .requestMatchers("/api/user/**").hasRole("USER")
                    .anyRequest().authenticated()
                )
                .oauth2ResourceServer(oauth2 -> oauth2
                    .jwt(jwt -> jwt.jwtAuthenticationConverter(jwtConverter))
                ); // Валидация JWT-токенов через Keycloak
            return http.build();
        }
    }
  4. Защита эндпоинта:

    @RestController
    @RequestMapping("/api/user")
    public class UserController {
    
        @GetMapping("/profile")
        // @PreAuthorize("hasRole('USER')") // Альтернатива конфигурации в SecurityFilterChain
        public String getUserProfile() {
            return "User profile data";
        }
    }

Как это работает:

  1. Клиент (frontend) аутентифицируется в Keycloak и получает JWT-токен (Access Token).
  2. Клиент отправляет этот токен в заголовке Authorization: Bearer <token> запроса к Spring Boot приложению.
  3. Spring Security (oauth2ResourceServer) проверяет подпись токена, извлекает claims (роли, имя пользователя) и создает объект аутентификации.
  4. На основе ролей из токена (resource_access.<client>.roles) принимается решение о доступе к эндпоинту.

Преимущества: Централизованное управление пользователями и правами, поддержка SSO, стандартные протоколы безопасности.