Рубрики
Без рубрики

Keycloak, встроенный в приложение Spring Boot

Узнайте, как настроить предварительно настроенный встроенный сервер Keycloak, который можно загрузить в приложении Spring Boot.

Автор оригинала: baeldung.

1. Обзор

Keycloak-это решение для управления идентификацией и доступом с открытым исходным кодом, администрируемое Red Hat и разработанное на Java компанией JBoss.

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

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

2. Предварительная настройка ключа

Для начала давайте разберемся, как мы можем предварительно настроить сервер блокировки ключей.

Сервер содержит набор областей, каждая из которых действует как изолированная единица для управления пользователями. Чтобы предварительно настроить его, нам нужно указать файл определения области в формате JSON.

Все, что можно настроить с помощью Консоль администратора Keycloak сохраняется в этом JSON.

Наш Сервер авторизации будет предварительно настроен с помощью baeldung-realm.json . Давайте посмотрим несколько соответствующих конфигураций в файле:

  • пользователи : наши пользователи по умолчанию будут [email protected] и [email protected] ; у них также будут свои учетные данные здесь
  • клиенты : мы определим клиента с идентификатором новый клиент
  • standardFlowEnabled : установите значение true, чтобы активировать поток кода авторизации для NewClient
  • redirecturls : новый клиент ‘, на которые сервер перенаправит после успешной аутентификации, перечислены здесь
  • web Origins : установите значение “+” , чтобы разрешить поддержку CORS для всех URL-адресов, перечисленных как URL-адреса перенаправления

Сервер Keycloak по умолчанию выдает токены JWT, поэтому для этого не требуется отдельная конфигурация. Давайте теперь рассмотрим конфигурации Maven.

3. Конфигурация Maven

Поскольку мы встроим Keycloak в приложение Spring Boot, нет необходимости загружать его отдельно.

Вместо этого мы установим следующий набор зависимостей :


    org.springframework.boot        
    spring-boot-starter-web



    org.springframework.boot
    spring-boot-starter-actuator



    org.springframework.boot
    spring-boot-starter-data-jpa



    com.h2database
    h2
    runtime

       

Обратите внимание, что мы используем версию 2.2.6.RELEASE Spring Boot здесь. Зависимости spring-boot-starter-data-jpa и H2 были добавлены для сохранения. Другой springframework.зависимости boot предназначены для веб-поддержки, так как мы также должны иметь возможность запускать сервер авторизации Keycloak, а также консоль администратора в качестве веб-служб.

Нам также понадобится пара зависимостей для Keycloak и БУДЬТЕ спокойны :


    org.jboss.resteasy
    resteasy-jackson2-provider
    3.12.1.Final



    org.keycloak
    keycloak-dependencies-server-all
    11.0.2
    pom
 

Проверьте сайт Maven на наличие последних версий Keycloak и REST Easy .

И, наконец, мы должны переопределить свойство , чтобы использовать версию, объявленную Keycloak, вместо версии, определенной Spring Boot:


    10.1.8.Final

4. Встроенная конфигурация ключа

Теперь давайте определим конфигурацию Spring для нашего сервера авторизации:

@Configuration
public class EmbeddedKeycloakConfig {

    @Bean
    ServletRegistrationBean keycloakJaxRsApplication(
      KeycloakServerProperties keycloakServerProperties, DataSource dataSource) throws Exception {
        
        mockJndiEnvironment(dataSource);
        EmbeddedKeycloakApplication.keycloakServerProperties = keycloakServerProperties;
        ServletRegistrationBean servlet = new ServletRegistrationBean<>(
          new HttpServlet30Dispatcher());
        servlet.addInitParameter("javax.ws.rs.Application", 
          EmbeddedKeycloakApplication.class.getName());
        servlet.addInitParameter(ResteasyContextParameters.RESTEASY_SERVLET_MAPPING_PREFIX,
          keycloakServerProperties.getContextPath());
        servlet.addInitParameter(ResteasyContextParameters.RESTEASY_USE_CONTAINER_FORM_PARAMS, 
          "true");
        servlet.addUrlMappings(keycloakServerProperties.getContextPath() + "/*");
        servlet.setLoadOnStartup(1);
        servlet.setAsyncSupported(true);
        return servlet;
    }

    @Bean
    FilterRegistrationBean keycloakSessionManagement(
      KeycloakServerProperties keycloakServerProperties) {
        FilterRegistrationBean filter = new FilterRegistrationBean<>();
	filter.setName("Keycloak Session Management");
	filter.setFilter(new EmbeddedKeycloakRequestFilter());
	filter.addUrlPatterns(keycloakServerProperties.getContextPath() + "/*");

	return filter;
    }

    private void mockJndiEnvironment(DataSource dataSource) throws NamingException {		 
        NamingManager.setInitialContextFactoryBuilder(
          (env) -> (environment) -> new InitialContext() {
            @Override
            public Object lookup(Name name) {
                return lookup(name.toString());
            }
	
            @Override
            public Object lookup(String name) {
                if ("spring/datasource".equals(name)) {
                    return dataSource;
                }
                return null;
            }

            @Override
            public NameParser getNameParser(String name) {
                return CompositeName::new;
            }

            @Override
            public void close() {
            }
        });
    }
}

Примечание: не беспокойтесь об ошибке компиляции, мы определим Встроенный фильтр запросов Keycloak класс позже.

Как мы можем видеть здесь, мы сначала настроили Keycloak как приложение JAX-RS с Свойствами сервера Keycloak для постоянного хранения свойств Keycloak, указанных в нашем файле определения области. Затем мы добавили фильтр sessionmanagementfilter и издевались над средой JNDI , чтобы использовать spring/datasource , которая является нашей базой данных H2 в памяти.

5. Свойства сервера Keycloak

Теперь давайте посмотрим на свойства сервера Keycloak , о которых мы только что упоминали:

@ConfigurationProperties(prefix = "keycloak.server")
public class KeycloakServerProperties {
    String contextPath = "/auth";
    String realmImportFile = "baeldung-realm.json";
    AdminUser adminUser = new AdminUser();

    // getters and setters

    public static class AdminUser {
        String username = "admin";
        String password = "admin";

        // getters and setters        
    }
}

Как мы видим, это простой POJO для установки ContextPath , admin User и файла определения области .

6. Встроенное приложение KeycloakApplication

Далее давайте рассмотрим класс, который использует конфигурации, заданные нами ранее, для создания областей:

public class EmbeddedKeycloakApplication extends KeycloakApplication {
    private static final Logger LOG = LoggerFactory.getLogger(EmbeddedKeycloakApplication.class);
    static KeycloakServerProperties keycloakServerProperties;

    protected void loadConfig() {
        JsonConfigProviderFactory factory = new RegularJsonConfigProviderFactory();
        Config.init(factory.create()
          .orElseThrow(() -> new NoSuchElementException("No value present")));
    }
    public EmbeddedKeycloakApplication() {
        createMasterRealmAdminUser();
        createBaeldungRealm();
    }

    private void createMasterRealmAdminUser() {
        KeycloakSession session = getSessionFactory().create();
        ApplianceBootstrap applianceBootstrap = new ApplianceBootstrap(session);
        AdminUser admin = keycloakServerProperties.getAdminUser();
        try {
            session.getTransactionManager().begin();
            applianceBootstrap.createMasterRealmUser(admin.getUsername(), admin.getPassword());
            session.getTransactionManager().commit();
        } catch (Exception ex) {
            LOG.warn("Couldn't create keycloak master admin user: {}", ex.getMessage());
            session.getTransactionManager().rollback();
        }
        session.close();
    }

    private void createBaeldungRealm() {
        KeycloakSession session = getSessionFactory().create();
        try {
            session.getTransactionManager().begin();
            RealmManager manager = new RealmManager(session);
            Resource lessonRealmImportFile = new ClassPathResource(
              keycloakServerProperties.getRealmImportFile());
            manager.importRealm(JsonSerialization.readValue(lessonRealmImportFile.getInputStream(),
              RealmRepresentation.class));
            session.getTransactionManager().commit();
        } catch (Exception ex) {
            LOG.warn("Failed to import Realm json file: {}", ex.getMessage());
            session.getTransactionManager().rollback();
        }
        session.close();
    }
}

7. Пользовательские Реализации Платформы

Как мы уже говорили, Keycloak разработан Red Hat/JBoss. Поэтому он предоставляет функциональные возможности и библиотеки расширений для развертывания приложения на сервере Wildfly или в качестве решения Quarkus.

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

Например, в Embedded KeycloakApplication мы только что настроили , мы сначала загрузили конфигурацию сервера Keycloak keycloak-server.json , используя пустой подкласс абстрактного JsonConfigProviderFactory :

public class RegularJsonConfigProviderFactory extends JsonConfigProviderFactory { }

Затем мы расширили KeycloakApplication , чтобы создать две области: master и baeldung . Они создаются в соответствии со свойствами, указанными в нашем файле определения области, baeldung-realm.json .

Как вы можете видеть, мы используем KeycloakSession для выполнения всех транзакций, и для того, чтобы это работало должным образом, нам пришлось создать пользовательский AbstractRequestFilter ( EmbeddedKeycloakRequestFilter ) и настроить компонент для этого с помощью KeycloakSessionServletFilter в файле EmbeddedKeycloakConfig .

Кроме того, нам нужна пара пользовательских поставщиков, чтобы у нас были собственные реализации org.keycloak.common.util.ResteasyProvider и org.keycloak.platform.PlatformProvider и не полагайтесь на внешние зависимости.

Важно отметить, что информация об этих пользовательских поставщиках должна быть включена в папку META-INF/services проекта, чтобы они были собраны во время выполнения.

8. Сведение Всего Этого Воедино

Как мы видели, Keycloak значительно упростил необходимые конфигурации со стороны приложения . Нет необходимости программно определять источник данных или какие-либо конфигурации безопасности.

Чтобы свести все это воедино, нам нужно определить конфигурацию для Spring и приложения Spring Boot.

8.1. application.yml

Мы будем использовать простой YAML для конфигураций пружин:

server:
  port: 8083

spring:
  datasource:
    username: sa
    url: jdbc:h2:mem:testdb

keycloak:
  server:
    contextPath: /auth
    adminUser:
      username: bael-admin
      password: ********
    realmImportFile: baeldung-realm.json

8.2. Применение пружинной загрузки

Наконец, вот приложение Spring Boot:

@SpringBootApplication(exclude = LiquibaseAutoConfiguration.class)
@EnableConfigurationProperties(KeycloakServerProperties.class)
public class AuthorizationServerApp {
    private static final Logger LOG = LoggerFactory.getLogger(AuthorizationServerApp.class);
    
    public static void main(String[] args) throws Exception {
        SpringApplication.run(AuthorizationServerApp.class, args);
    }

    @Bean
    ApplicationListener onApplicationReadyEventListener(
      ServerProperties serverProperties, KeycloakServerProperties keycloakServerProperties) {
        return (evt) -> {
            Integer port = serverProperties.getPort();
            String keycloakContextPath = keycloakServerProperties.getContextPath();
            LOG.info("Embedded Keycloak started: http://localhost:{}{} to use keycloak", 
              port, keycloakContextPath);
        };
    }
}

Примечательно, что здесь мы включили свойства Keycloak Server configuration, чтобы ввести его в ApplicationListener bean.

После запуска этого класса мы можем получить доступ к странице приветствия сервера авторизации по адресу http://localhost:8083/auth/ .

9. Заключение

В этом кратком руководстве мы рассмотрели, как настроить сервер Keycloak, встроенный в приложение Spring Boot. Исходный код этого приложения доступен на GitHub .

Оригинальная идея для этой реализации была разработана Томасом Даримонтом и может быть найдена в проекте embedded-spring-boot-keycloak-server .