diff --git a/spring-boot-modules/spring-boot-keycloak/pom.xml b/spring-boot-modules/spring-boot-keycloak/pom.xml index 9e39176765..4f30d32bec 100644 --- a/spring-boot-modules/spring-boot-keycloak/pom.xml +++ b/spring-boot-modules/spring-boot-keycloak/pom.xml @@ -47,6 +47,10 @@ spring-boot-starter-test test + + org.springframework.boot + spring-boot-starter-oauth2-client + org.springframework.boot spring-boot-starter-security diff --git a/spring-boot-modules/spring-boot-keycloak/src/main/java/com/baeldung/keycloak/KeycloakLogoutHandler.java b/spring-boot-modules/spring-boot-keycloak/src/main/java/com/baeldung/keycloak/KeycloakLogoutHandler.java new file mode 100644 index 0000000000..06c41e9b1d --- /dev/null +++ b/spring-boot-modules/spring-boot-keycloak/src/main/java/com/baeldung/keycloak/KeycloakLogoutHandler.java @@ -0,0 +1,45 @@ +package com.baeldung.keycloak; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.core.oidc.user.OidcUser; +import org.springframework.security.web.authentication.logout.LogoutHandler; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@Component +public class KeycloakLogoutHandler implements LogoutHandler { + + private static final Logger logger = LoggerFactory.getLogger(KeycloakLogoutHandler.class); + private final RestTemplate restTemplate; + + public KeycloakLogoutHandler(RestTemplate restTemplate) { + this.restTemplate = restTemplate; + } + + @Override + public void logout(HttpServletRequest request, HttpServletResponse response, Authentication auth) { + logoutFromKeycloak((OidcUser) auth.getPrincipal()); + } + + private void logoutFromKeycloak(OidcUser user) { + String endSessionEndpoint = user.getIssuer() + "/protocol/openid-connect/logout"; + UriComponentsBuilder builder = UriComponentsBuilder + .fromUriString(endSessionEndpoint) + .queryParam("id_token_hint", user.getIdToken().getTokenValue()); + + ResponseEntity logoutResponse = restTemplate.getForEntity(builder.toUriString(), String.class); + if (logoutResponse.getStatusCode().is2xxSuccessful()) { + logger.info("Successfulley logged out from Keycloak"); + } else { + logger.error("Could not propagate logout to Keycloak"); + } + } + +} diff --git a/spring-boot-modules/spring-boot-keycloak/src/main/java/com/baeldung/keycloak/SecurityConfig.java b/spring-boot-modules/spring-boot-keycloak/src/main/java/com/baeldung/keycloak/SecurityConfig.java index 826f475a6e..88f829e567 100644 --- a/spring-boot-modules/spring-boot-keycloak/src/main/java/com/baeldung/keycloak/SecurityConfig.java +++ b/spring-boot-modules/spring-boot-keycloak/src/main/java/com/baeldung/keycloak/SecurityConfig.java @@ -1,42 +1,41 @@ package com.baeldung.keycloak; -import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver; -import org.keycloak.adapters.springsecurity.KeycloakConfiguration; -import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider; -import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.session.SessionRegistryImpl; import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy; import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; -@KeycloakConfiguration -class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter { - // Submits the KeycloakAuthenticationProvider to the AuthenticationManager - @Autowired - public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { - KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider(); - keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper()); - auth.authenticationProvider(keycloakAuthenticationProvider); +@Configuration +@EnableWebSecurity +class SecurityConfig extends WebSecurityConfigurerAdapter { + + private final KeycloakLogoutHandler keycloakLogoutHandler; + + SecurityConfig(KeycloakLogoutHandler keycloakLogoutHandler) { + this.keycloakLogoutHandler = keycloakLogoutHandler; } - // Specifies the session authentication strategy @Bean - @Override protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl()); } @Override protected void configure(HttpSecurity http) throws Exception { - super.configure(http); http.authorizeRequests() - .antMatchers("/customers*", "/users*") - .hasRole("user") - .anyRequest() - .permitAll(); + .antMatchers("/customers*", "/users*") + .hasRole("USER") + .anyRequest() + .permitAll(); + http.oauth2Login() + .and() + .logout() + .addLogoutHandler(keycloakLogoutHandler) + .logoutSuccessUrl("/"); } + } diff --git a/spring-boot-modules/spring-boot-keycloak/src/main/java/com/baeldung/keycloak/SpringBoot.java b/spring-boot-modules/spring-boot-keycloak/src/main/java/com/baeldung/keycloak/SpringBoot.java index d67dd05fc7..90d7e774a4 100644 --- a/spring-boot-modules/spring-boot-keycloak/src/main/java/com/baeldung/keycloak/SpringBoot.java +++ b/spring-boot-modules/spring-boot-keycloak/src/main/java/com/baeldung/keycloak/SpringBoot.java @@ -2,6 +2,8 @@ package com.baeldung.keycloak; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.web.client.RestTemplate; @SpringBootApplication @@ -11,4 +13,8 @@ public class SpringBoot { SpringApplication.run(SpringBoot.class, args); } + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } } diff --git a/spring-boot-modules/spring-boot-keycloak/src/main/resources/application.properties b/spring-boot-modules/spring-boot-keycloak/src/main/resources/application.properties index 9dfd3ea720..323617e2ef 100644 --- a/spring-boot-modules/spring-boot-keycloak/src/main/resources/application.properties +++ b/spring-boot-modules/spring-boot-keycloak/src/main/resources/application.properties @@ -6,6 +6,10 @@ keycloak.auth-server-url=http://localhost:8180/auth keycloak.realm=SpringBootKeycloak keycloak.resource=login-app keycloak.public-client=true -#keycloak.security-constraints[0].authRoles[0]=user -#keycloak.security-constraints[0].securityCollections[0].patterns[0]=/customers/* -keycloak.principal-attribute=preferred_username \ No newline at end of file +keycloak.principal-attribute=preferred_username + +spring.security.oauth2.client.registration.keycloak.client-id=login-app +spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code +spring.security.oauth2.client.registration.keycloak.scope=openid +spring.security.oauth2.client.provider.keycloak.issuer-uri=http://localhost:8180/auth/realms/SpringBootKeycloak +spring.security.oauth2.client.provider.keycloak.user-name-attribute=preferred_username \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-keycloak/src/test/java/com/baeldung/SpringContextTest.java b/spring-boot-modules/spring-boot-keycloak/src/test/java/com/baeldung/SpringContextTest.java deleted file mode 100644 index 3f3ecd87d0..0000000000 --- a/spring-boot-modules/spring-boot-keycloak/src/test/java/com/baeldung/SpringContextTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.baeldung; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; - -import com.baeldung.keycloak.SpringBoot; - -@RunWith(SpringRunner.class) -@SpringBootTest(classes = SpringBoot.class) -public class SpringContextTest { - - @Test - public void whenSpringContextIsBootstrapped_thenNoExceptions() { - } -}