Keycloak innebygd i en Spring Boot-applikasjon

1. Oversikt

Keycloak er en åpen kildekode-løsning for identitets- og tilgangshåndtering administrert av RedHat, og utviklet i Java av JBoss.

I denne opplæringen lærer vi hvordan du setter opp en Keycloak-server innebygd i et Spring Boot-program. Dette gjør det enkelt å starte en forhåndskonfigurert Keycloak-server.

Keycloak kan også kjøres som en frittstående server, men da innebærer det nedlasting og oppsett via administrasjonskonsollen.

2. Forhåndskonfigurasjon av nøkkelring

Til å begynne med, la oss forstå hvordan vi kan forhåndskonfigurere en Keycloak-server.

Serveren inneholder et sett med riker, hvor hvert rike fungerer som en isolert enhet for brukeradministrasjon. For å forhåndskonfigurere den, må vi spesifisere en realm-definisjonsfil i JSON-format.

Alt som kan konfigureres ved hjelp av Keycloak Admin Console er vedvarende i denne JSON.

Autorisasjonsserveren vår blir forhåndskonfigurert med baeldung-realm.json. La oss se noen relevante konfigurasjoner i filen:

  • brukere: Standardbrukerne våre ville være det [e-postbeskyttet] og [e-postbeskyttet]; de vil også ha sin legitimasjon her
  • klienter: vi definerer en klient med id newClient
  • standardFlowEnabled: sett til sant for å aktivere autorisasjonskodestrøm for newClient
  • redirectUris: newClientHer vises nettadressene som serveren vil omdirigere til etter vellykket godkjenning
  • webOrigins: satt til “+” for å tillate CORS-støtte for alle nettadresser som er oppført som redirectUris

Keycloak-serveren utsteder JWT-tokens som standard, så det er ingen egen konfigurasjon nødvendig for det. La oss se på Maven-konfigurasjonene neste.

3. Maven-konfigurasjon

Siden vi legger inn Keycloak inne i en Spring Boot-applikasjon, er det ikke nødvendig å laste den ned separat.

I stedet, vi setter opp følgende sett med avhengigheter:

 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 

Merk at vi bruker Spring Boot's 2.2.6.RELEASE-versjon her. Avhengighetene spring-boot-starter-data-jpa og H2 er tilsatt for utholdenhet. Den andre springframework.boot avhengigheter er for nettstøtte, ettersom vi også trenger å kunne kjøre Keycloak autorisasjonsserver samt administrasjonskonsoll som webtjenester.

Vi trenger også et par avhengigheter for Keycloak og RESTEasy:

 org.jboss.resteasy resteasy-jackson2-leverandør 3.12.1.Final org.keycloak keycloak-avhengigheter-server-all 11.0.2 pom 

Sjekk Maven-nettstedet for de nyeste versjonene av Keycloak og RESTEasy.

Og til slutt må vi overstyre eiendom, for å bruke versjonen deklarert av Keycloak i stedet for den som er definert av Spring Boot:

 10.1.8.Final 

4. Embedded Keycloak Configuration

La oss nå definere vårkonfigurasjonen for autorisasjonsserveren vår:

@Configuration public class EmbeddedKeycloakConfig {@Bean ServletRegistrationBean keycloakJaxRsApplication (KeycloakServerProperties keycloakServerProperties, DataSource dataSource) kaster Unntak {mockJndiEnvironment (dataSource); EmbeddedKeycloakApplication.keycloakServerProperties = keycloakServerProperties; ServletRegistrationBean servlet = ny ServletRegistrationBean (ny 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); retur servlet; } @Bean FilterRegistrationBean keycloakSessionManagement (KeycloakServerProperties keycloakServerProperties) {FilterRegistrationBean filter = new FilterRegistrationBean (); filter.setName ("Keycloak Session Management"); filter.setFilter (ny EmbeddedKeycloakRequestFilter ()); filter.addUrlPatterns (keycloakServerProperties.getContextPath () + "/ *"); returfilter; } private void mockJndiEnvironment (DataSource dataSource) kaster NamingException {NamingManager.setInitialContextFactoryBuilder ((env) -> (miljø) -> ny InitialContext () {@Override offentlig objektoppslag (Navn navn) {returoppslag (navn.toString ()); } @Override offentlig objektoppslag (strengnavn) {if ("spring / datasource" .equals (name)) {return dataSource;} return null;} @Override public NameParser getNameParser (strengnavn) {return CompositeName :: new;} @Override public void close () {}}); }} 

Merk: ikke bekymre deg for kompileringsfeilen, vi definerer EmbeddedKeycloakRequestFilter klasse senere.

Som vi kan se her, konfigurerte vi først Keycloak som en JAX-RS-applikasjon med KeycloakServerProperties for vedvarende lagring av Keycloak-egenskaper som spesifisert i vår definisjonsfil. Vi la til et sesjonsstyringsfilter og spottet et JNDI-miljø for å bruke et vår / datakilde, som er vår H2-database i minnet.

5. KeycloakServerProperties

La oss nå ta en titt på KeycloakServerProperties vi nettopp nevnte:

@ConfigurationProperties (prefix = "keycloak.server") offentlig klasse KeycloakServerProperties {String contextPath = "/ auth"; String realmImportFile = "baeldung-realm.json"; AdminUser adminUser = ny AdminUser (); // getters and setters public static class AdminUser {String username = "admin"; Strengpassord = "admin"; // getters og setters}} 

Som vi kan se, dette er en enkel POJO å stille inn kontekststi, adminbruker og rikets definisjonsfil.

6. EmbeddedKeycloakApplication

La oss deretter se klassen, som bruker konfigurasjonene vi satte før, for å skape riker:

offentlig klasse EmbeddedKeycloakApplication utvider KeycloakApplication {private static final Logger LOG = LoggerFactory.getLogger (EmbeddedKeycloakApplication.class); statisk KeycloakServerProperties keycloakServerProperties; beskyttet tomrom loadConfig () {JsonConfigProviderFactory fabrikk = ny RegularJsonConfigProviderFactory (); Config.init (factory.create () .orElseThrow (() -> new NoSuchElementException ("No value present"))); } offentlig EmbeddedKeycloakApplication () {createMasterRealmAdminUser (); createBaeldungRealm (); } privat tomrom createMasterRealmAdminUser () {KeycloakSession session = getSessionFactory (). create (); ApplianceBootstrap applianceBootstrap = ny ApplianceBootstrap (økt); AdminUser admin = keycloakServerProperties.getAdminUser (); prøv {session.getTransactionManager (). begynn (); applianceBootstrap.createMasterRealmUser (admin.getUsername (), admin.getPassword ()); session.getTransactionManager (). commit (); } catch (Exception ex) {LOG.warn ("Kunne ikke opprette hovedadministratorbruker for keycloak: {}", ex.getMessage ()); session.getTransactionManager (). tilbakeføring (); } session.close (); } privat tomrom createBaeldungRealm () {KeycloakSession session = getSessionFactory (). create (); prøv {session.getTransactionManager (). begynn (); RealmManager manager = ny RealmManager (økt); Resource lessonRealmImportFile = ny ClassPathResource (keycloakServerProperties.getRealmImportFile ()); manager.importRealm (JsonSerialization.readValue (lessonRealmImportFile.getInputStream (), RealmRepresentation.class)); session.getTransactionManager (). commit (); } catch (Unntak ex) {LOG.warn ("Kunne ikke importere Realm json-fil: {}", ex.getMessage ()); session.getTransactionManager (). tilbakeføring (); } session.close (); }} 

7. Tilpassede plattformimplementasjoner

Som vi sa er Keycloak utviklet av RedHat / JBoss. Derfor gir den funksjonalitets- og utvidelsesbiblioteker for å distribuere applikasjonen på en Wildfly-server, eller som en Quarkus-løsning.

I dette tilfellet beveger vi oss bort fra disse alternativene, og som en konsekvens må vi tilby tilpassede implementeringer for noen plattformspesifikke grensesnitt og klasser.

For eksempel i EmbeddedKeycloakApplication vi konfigurerte nettopp at vi først lastet inn Keycloaks serverkonfigurasjon keycloak-server.json, ved hjelp av en tom underklasse av abstraktet JsonConfigProviderFactory:

offentlig klasse RegularJsonConfigProviderFactory utvider JsonConfigProviderFactory {}

Så forlenget vi KeycloakApplication å skape to riker: herre og baeldung. Disse opprettes i henhold til egenskapene som er spesifisert i vår definisjonsfil baeldung-realm.json.

Som du ser, bruker vi en KeycloakSession for å utføre alle transaksjonene, og for at dette skulle fungere ordentlig, måtte vi lage en tilpasset AbstractRequestFilter (EmbeddedKeycloakRequestFilter) og sett opp en bønne for dette ved hjelp av a KeycloakSessionServletFilter i EmbeddedKeycloakConfig fil.

I tillegg trenger vi et par tilpassede leverandører slik at vi har våre egne implementeringer av org.keycloak.common.util.ResteasyProvider og org.keycloak.platform.PlatformProvider og ikke stole på eksterne avhengigheter.

Det er viktig at informasjon om disse tilpassede leverandørene skal inkluderes i prosjektets META-INF / tjenester mappen slik at de blir plukket opp ved kjøretid.

8. Å bringe det hele sammen

Som vi så, Keycloak har mye forenklet de nødvendige konfigurasjonene fra applikasjonssiden. Det er ikke behov for å programmatisk definere datakilden eller noen sikkerhetskonfigurasjoner.

For å bringe alt sammen, må vi definere konfigurasjonen for Spring og en Spring Boot Application.

8.1. application.yml

Vi bruker en enkel YAML for vårkonfigurasjonene:

server: port: 8083 vår: datakilde: brukernavn: sa url: jdbc: h2: mem: testdb keycloak: server: contextPath: / auth admin Bruker: brukernavn: bael-admin passord: ******** realmImportFile: baeldung- realm.json

8.2. Spring Boot Application

Til slutt, her er Spring Boot Application:

@SpringBootApplication (ekskluder = LiquibaseAutoConfiguration.class) @EnableConfigurationProperties (KeycloakServerProperties.class) public class AuthorizationServerApp {private static final Logger LOG = LoggerFactory.getLogger (AuthorizationServerApp.class); offentlig statisk ugyldig hoved (String [] args) kaster Unntak {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); }; }}

Spesielt her har vi aktivert KeycloakServerProperties konfigurasjonen for å injisere den i ApplicationListener bønne.

Etter å ha kjørt denne klassen, vi kan få tilgang til autorisasjonsserverens velkomstside på // localhost: 8083 / auth /.

9. Konklusjon

I denne raske opplæringen så vi hvordan du konfigurerer en Keycloak-server innebygd i et Spring Boot-program. Kildekoden for dette programmet er tilgjengelig på GitHub.

Den opprinnelige ideen for denne implementeringen ble utviklet av Thomas Darimont og kan bli funnet i prosjektet innebygd-spring-boot-keycloak-server.