Keycloak, вграден в приложение за пролетно зареждане

1. Общ преглед

Keycloak е решение за управление на идентичност и достъп с отворен код, администрирано от RedHat и разработено в Java от JBoss.

В този урок ще научим как да настроим сървър на Keycloak, вграден в приложение за Spring Boot . Това улеснява стартирането на предварително конфигуриран сървър Keycloak.

Keycloak може да се изпълнява и като самостоятелен сървър, но след това включва изтеглянето му и настройката чрез Admin Console.

2. Предварителна конфигурация на клавиатурата

Като начало, нека разберем как можем да конфигурираме предварително сървър на Keycloak.

Сървърът съдържа набор от сфери, като всяко царство действа като изолирана единица за управление на потребителите. За да го конфигурираме предварително, трябва да посочим файл за дефиниция на царство във формат JSON.

Всичко, което може да бъде конфигурирано с помощта на администраторската конзола Keycloak, се запазва в този JSON.

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

  • потребители : нашите потребители по подразбиране ще бъдат [защитени по имейл] и [защитени по имейл] ; те също ще имат своите пълномощия тук
  • клиенти : ще определим клиент с идентификатора newClient
  • standardFlowEnabled : задава се на true, за да активира Поток на разрешителния код за newClient
  • redirectUris : тук са изброени URL адресите на newClient , към които сървърът ще пренасочи след успешно удостоверяване
  • webOrigins : задайте „+“, за да позволите поддръжка на CORS за всички URL адреси, изброени като redirectUris

Сървърът 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 и RESTEasy :

 org.jboss.resteasy resteasy-jackson2-provider 3.12.1.Final   org.keycloak keycloak-dependencies-server-all 11.0.2 pom  

Проверете сайта Maven за най-новите версии на Keycloak и RESTEasy.

И накрая, трябва да заменим , за да използвате версията, декларирана от 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() { } }); } } 

Забележка: не се притеснявайте от грешката при компилацията, по-късно ще определим класа EmbeddedKeycloakRequestFilter .

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

5. KeycloakServerProperties

Сега нека разгледаме току-що споменатите KeycloakServerProperties :

@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 , adminUser и дефиницията на царството .

6. EmbeddedKeycloakApplication

След това нека видим класа, който използва конфигурациите, които сме задали преди, за създаване на области:

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 е разработен от RedHat / JBoss. Поради това той предоставя функционалности и библиотеки с разширения за разполагане на приложението на сървър Wildfly или като решение на Quarkus.

В този случай се отдалечаваме от тези алтернативи и в резултат на това трябва да предоставим персонализирани реализации за някои специфични за платформата интерфейси и класове.

Например, в EmbeddedKeycloakApplication, която току-що конфигурирахме, първо заредихме конфигурацията на сървъра на 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 / услуги на проекта, така че те да бъдат взети по време на изпълнение.

8. Събиране на всичко

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

За да обединим всичко, трябва да дефинираме конфигурацията за Spring и Spring Boot Application.

8.1. приложение.имм

Ще използваме прост YAML за конфигурациите на Spring:

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: //localhost:{}{} to use keycloak", port, keycloakContextPath); }; } }

За отбелязване е, че тук сме активирали конфигурацията KeycloakServerProperties , за да я инжектира в зърното ApplicationListener .

След като стартираме този клас, можем да осъществим достъп до началната страница на сървъра за оторизация на // localhost: 8083 / auth / .

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

В този бърз урок видяхме как да настроим сървър на Keycloak, вграден в приложение за Spring Boot. Изходният код за това приложение е достъпен в GitHub.

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