Keycloak Disertakan dalam Aplikasi Spring Boot

1. Gambaran keseluruhan

Keycloak adalah penyelesaian Pengurusan Identiti dan Akses sumber terbuka yang ditadbir oleh RedHat, dan dikembangkan di Java oleh JBoss.

Dalam tutorial ini, kita akan belajar bagaimana menyediakan pelayan Keycloak yang disematkan dalam aplikasi Spring Boot . Ini memudahkan anda memulakan pelayan Keycloak yang telah dikonfigurasi sebelumnya.

Keycloak juga dapat dijalankan sebagai pelayan mandiri, tetapi kemudian melibatkan memuat turunnya dan penyediaan melalui Konsol Pentadbiran.

2. Pra-Konfigurasi Keycloak

Sebagai permulaan, mari kita fahami bagaimana kita boleh membuat pra-konfigurasi pelayan Keycloak.

Pelayan mengandungi sekumpulan alam, dengan setiap bidang bertindak sebagai unit terpencil untuk pengurusan pengguna. Untuk mengkonfigurasinya terlebih dahulu, kita perlu menentukan fail definisi ranah dalam format JSON.

Semua yang boleh dikonfigurasi menggunakan Konsol Pentadbiran Keycloak ada di JSON ini.

Pelayan Pengesahan kami akan dikonfigurasikan sebelumnya dengan baeldung-realm.json . Mari lihat beberapa konfigurasi yang berkaitan dalam fail:

  • pengguna : pengguna lalai kami adalah [dilindungi e-mel] dan [dilindungi e-mel] ; mereka juga akan mempunyai tauliah di sini
  • pelanggan : kami akan menentukan pelanggan dengan id newClient
  • standardFlowEnabled : tetapkan ke true untuk mengaktifkan Aliran Kod Pengesahan untuk pelanggan baru
  • redirectUris : newClient URL 's pelayan ini akan redirect ke selepas pengesahan berjaya disenaraikan di sini
  • webOrigins : tetapkan ke "+" untuk membolehkan sokongan CORS untuk semua URL yang disenaraikan sebagai redirectUris

Pelayan Keycloak mengeluarkan token JWT secara lalai, jadi tidak ada konfigurasi berasingan yang diperlukan untuk itu. Mari lihat konfigurasi Maven seterusnya.

3. Konfigurasi Maven

Oleh kerana kami akan memasukkan Keycloak di dalam aplikasi Spring Boot, tidak perlu memuat turunnya secara berasingan.

Sebagai gantinya, kami akan menyediakan set kebergantungan berikut :

 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  

Perhatikan bahawa kami menggunakan versi Spring 2.2 2.2. SIARAN di sini. Ketergantungan spring-boot-starter-data-jpa dan H2 telah ditambahkan untuk kegigihan. Ketergantungan springframework.boot yang lain adalah untuk sokongan web, kerana kami juga harus dapat menjalankan pelayan kebenaran Keycloak dan juga konsol pentadbir sebagai perkhidmatan web.

Kami juga memerlukan beberapa pergantungan untuk Keycloak dan RESTEasy :

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

Periksa laman Maven untuk versi terbaru dari Keycloak dan RESTEasy.

Dan akhirnya, kita harus mengatasi harta tanah, untuk menggunakan versi yang dinyatakan oleh Keycloak dan bukan versi yang ditentukan oleh Spring Boot:

 10.1.8.Final 

4. Konfigurasi Keycloak Tertanam

Sekarang mari kita tentukan konfigurasi Spring untuk pelayan kebenaran kami:

@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() { } }); } } 

Catatan: jangan bimbang tentang ralat penyusunan, kami akan menentukan kelas EmbeddedKeycloakRequestFilter di kemudian hari.

Seperti yang dapat kita lihat di sini, pertama kali kita mengkonfigurasi Keycloak sebagai aplikasi JAX-RS dengan KeycloakServerProperties untuk penyimpanan sifat Keycloak yang berterusan seperti yang ditentukan dalam fail definisi wilayah kita. Kami kemudian menambahkan saringan pengurusan sesi dan mengejek persekitaran JNDI untuk menggunakan sumber musim bunga / data , yang merupakan pangkalan data H2 dalam memori kami.

5. KeycloakServerProperties

Sekarang mari kita lihat KeycloakServerProperties yang baru sahaja kita nyatakan:

@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 } } 

Seperti yang dapat kita lihat, ini adalah POJO sederhana untuk menetapkan fail definisi konteksPath , adminUser dan realm .

6. Aplikasi EmbeddedKeycloak

Seterusnya, mari kita lihat kelas, yang menggunakan konfigurasi yang kita tetapkan sebelumnya, untuk membuat alam:

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. Pelaksanaan Platform Custom

Seperti yang kami katakan, Keycloak dikembangkan oleh RedHat / JBoss. Oleh itu, ia menyediakan fungsi dan perpustakaan perpanjangan untuk menyebarkan aplikasi pada pelayan Wildfly, atau sebagai penyelesaian Quarkus.

Dalam kes ini, kami menjauh dari alternatif tersebut, dan sebagai akibatnya, kami harus menyediakan pelaksanaan khusus untuk beberapa antara muka dan kelas khusus platform.

Sebagai contoh, dalam EmbeddedKeycloakApplication kami baru saja mengkonfigurasi kami memuatkan keycloak- server.json konfigurasi pelayan Keycloak , dengan menggunakan subkelas kosong JsonConfigProviderFactory abstrak :

public class RegularJsonConfigProviderFactory extends JsonConfigProviderFactory { }

Kemudian, kami memperluas KeycloakApplication untuk membuat dua alam: master dan baeldung . Ini dibuat mengikut sifat yang ditentukan dalam fail definisi wilayah kami, baeldung-realm.json .

Seperti yang anda lihat, kami menggunakan KeycloakSession untuk melakukan semua urus niaga, dan untuk ini berfungsi dengan baik, kami harus membuat AbstractRequestFilter ( EmbeddedKeycloakRequestFilter ) khusus dan menyiapkan kacang untuk ini menggunakan KeycloakSessionServletFilter dalam fail EmbeddedKeycloakConfig .

Selain itu, kami memerlukan beberapa penyedia tersuai supaya kami mempunyai implementasi sendiri dari org.keycloak.common.util.ResteasyProvider dan org.keycloak.platform.PlatformProvider dan tidak bergantung pada pergantungan luaran.

Importantly, information about these custom providers should be included in the project's META-INF/services folder so that they are picked up at runtime.

8. Bringing It All Together

As we saw, Keycloak has much simplified the required configurations from the application side. There is no need to programmatically define the datasource or any security configurations.

To bring it all together, we need to define the configuration for Spring and a Spring Boot Application.

8.1. application.yml

We'll be using a simple YAML for the Spring configurations:

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 Application

Lastly, here's the Spring Boot Application:

@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); }; } }

Di sini, kami telah mengaktifkan konfigurasi KeycloakServerProperties untuk memasukkannya ke dalam kacang ApplicationListener .

Setelah menjalankan kelas ini, kita dapat mengakses halaman selamat datang pelayan kebenaran di // localhost: 8083 / auth / .

9. Kesimpulannya

Dalam tutorial ringkas ini, kami melihat cara menyiapkan pelayan Keycloak yang tertanam dalam aplikasi Spring Boot. Kod sumber untuk aplikasi ini boleh didapati di GitHub.

Idea asal untuk pelaksanaan ini dikembangkan oleh Thomas Darimont dan boleh didapati dalam projek pelekat embedded-spring-boot-keycloak-server.