Skip to content

Commit dcb6c85

Browse files
authored
Merge pull request #13293 from anuragkumawat/java-14471
JAVA-14471 Update Keycloak articles - Added new module for keycloak adapters
2 parents 41d7305 + a53ee3f commit dcb6c85

27 files changed

Lines changed: 486 additions & 79 deletions
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
## Spring Boot Keycloak
2+
3+
This module contains articles about Keycloak in Spring Boot projects.
4+
5+
## Relevant articles:
6+
- [Custom User Attributes with Keycloak](https://www.baeldung.com/keycloak-custom-user-attributes)
7+
- [Get Keycloak User ID in Spring](https://www.baeldung.com/spring-keycloak-get-user-id)
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<groupId>com.baeldung.keycloak</groupId>
7+
<artifactId>spring-boot-keycloak-adapters</artifactId>
8+
<version>0.0.1</version>
9+
<name>spring-boot-keycloak-adapters</name>
10+
<packaging>jar</packaging>
11+
<description>This is a simple application demonstrating integration between Keycloak and Spring Boot.</description>
12+
13+
<parent>
14+
<groupId>com.baeldung</groupId>
15+
<artifactId>parent-boot-2</artifactId>
16+
<version>0.0.1-SNAPSHOT</version>
17+
<relativePath>../../parent-boot-2</relativePath>
18+
</parent>
19+
20+
<dependencyManagement>
21+
<dependencies>
22+
<dependency>
23+
<groupId>org.keycloak.bom</groupId>
24+
<artifactId>keycloak-adapter-bom</artifactId>
25+
<version>${keycloak-adapter-bom.version}</version>
26+
<type>pom</type>
27+
<scope>import</scope>
28+
</dependency>
29+
</dependencies>
30+
</dependencyManagement>
31+
32+
<dependencies>
33+
<dependency>
34+
<groupId>org.springframework.boot</groupId>
35+
<artifactId>spring-boot-starter</artifactId>
36+
</dependency>
37+
<dependency>
38+
<groupId>org.keycloak</groupId>
39+
<artifactId>keycloak-spring-boot-starter</artifactId>
40+
</dependency>
41+
<dependency>
42+
<groupId>org.springframework.boot</groupId>
43+
<artifactId>spring-boot-starter-data-jpa</artifactId>
44+
</dependency>
45+
<dependency>
46+
<groupId>org.springframework.boot</groupId>
47+
<artifactId>spring-boot-starter-test</artifactId>
48+
<scope>test</scope>
49+
</dependency>
50+
<dependency>
51+
<groupId>org.springframework.boot</groupId>
52+
<artifactId>spring-boot-starter-oauth2-client</artifactId>
53+
</dependency>
54+
<dependency>
55+
<groupId>org.springframework.boot</groupId>
56+
<artifactId>spring-boot-starter-security</artifactId>
57+
</dependency>
58+
<dependency>
59+
<groupId>org.springframework.boot</groupId>
60+
<artifactId>spring-boot-starter-web</artifactId>
61+
</dependency>
62+
<dependency>
63+
<groupId>org.hsqldb</groupId>
64+
<artifactId>hsqldb</artifactId>
65+
<scope>runtime</scope>
66+
</dependency>
67+
<dependency>
68+
<groupId>org.springframework.boot</groupId>
69+
<artifactId>spring-boot-starter-thymeleaf</artifactId>
70+
</dependency>
71+
<dependency>
72+
<groupId>org.springframework.security</groupId>
73+
<artifactId>spring-security-test</artifactId>
74+
<scope>test</scope>
75+
</dependency>
76+
</dependencies>
77+
78+
<build>
79+
<plugins>
80+
<plugin>
81+
<groupId>org.springframework.boot</groupId>
82+
<artifactId>spring-boot-maven-plugin</artifactId>
83+
</plugin>
84+
</plugins>
85+
</build>
86+
87+
<properties>
88+
<keycloak-adapter-bom.version>15.0.2</keycloak-adapter-bom.version>
89+
</properties>
90+
91+
</project>

spring-boot-modules/spring-boot-keycloak/src/main/java/com/baeldung/keycloak/CustomUserAttrController.java renamed to spring-boot-modules/spring-boot-keycloak-adapters/src/main/java/com/baeldung/keycloak/CustomUserAttrController.java

File renamed without changes.
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.baeldung.keycloak;
2+
3+
import javax.persistence.Entity;
4+
import javax.persistence.GeneratedValue;
5+
import javax.persistence.GenerationType;
6+
import javax.persistence.Id;
7+
8+
@Entity
9+
public class Customer {
10+
@Id
11+
@GeneratedValue(strategy = GenerationType.IDENTITY)
12+
private long id;
13+
private String name;
14+
private String serviceRendered;
15+
private String address;
16+
17+
public long getId() {
18+
return id;
19+
}
20+
21+
public void setId(long id) {
22+
this.id = id;
23+
}
24+
25+
public String getName() {
26+
return name;
27+
}
28+
29+
public void setName(String name) {
30+
this.name = name;
31+
}
32+
33+
public String getServiceRendered() {
34+
return serviceRendered;
35+
}
36+
37+
public void setServiceRendered(String serviceRendered) {
38+
this.serviceRendered = serviceRendered;
39+
}
40+
41+
public String getAddress() {
42+
return address;
43+
}
44+
45+
public void setAddress(String address) {
46+
this.address = address;
47+
}
48+
49+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.baeldung.keycloak;
2+
3+
import org.springframework.data.repository.CrudRepository;
4+
5+
public interface CustomerDAO extends CrudRepository<Customer, Long> {
6+
7+
}

spring-boot-modules/spring-boot-keycloak/src/main/java/com/baeldung/keycloak/KeycloakConfig.java renamed to spring-boot-modules/spring-boot-keycloak-adapters/src/main/java/com/baeldung/keycloak/KeycloakConfig.java

File renamed without changes.
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package com.baeldung.keycloak;
2+
3+
import org.slf4j.Logger;
4+
import org.slf4j.LoggerFactory;
5+
import org.springframework.http.ResponseEntity;
6+
import org.springframework.security.core.Authentication;
7+
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
8+
import org.springframework.security.web.authentication.logout.LogoutHandler;
9+
import org.springframework.stereotype.Component;
10+
import org.springframework.web.client.RestTemplate;
11+
import org.springframework.web.util.UriComponentsBuilder;
12+
13+
import javax.servlet.http.HttpServletRequest;
14+
import javax.servlet.http.HttpServletResponse;
15+
16+
@Component
17+
public class KeycloakLogoutHandler implements LogoutHandler {
18+
19+
private static final Logger logger = LoggerFactory.getLogger(KeycloakLogoutHandler.class);
20+
private final RestTemplate restTemplate;
21+
22+
public KeycloakLogoutHandler(RestTemplate restTemplate) {
23+
this.restTemplate = restTemplate;
24+
}
25+
26+
@Override
27+
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication auth) {
28+
logoutFromKeycloak((OidcUser) auth.getPrincipal());
29+
}
30+
31+
private void logoutFromKeycloak(OidcUser user) {
32+
String endSessionEndpoint = user.getIssuer() + "/protocol/openid-connect/logout";
33+
UriComponentsBuilder builder = UriComponentsBuilder
34+
.fromUriString(endSessionEndpoint)
35+
.queryParam("id_token_hint", user.getIdToken().getTokenValue());
36+
37+
ResponseEntity<String> logoutResponse = restTemplate.getForEntity(builder.toUriString(), String.class);
38+
if (logoutResponse.getStatusCode().is2xxSuccessful()) {
39+
logger.info("Successfulley logged out from Keycloak");
40+
} else {
41+
logger.error("Could not propagate logout to Keycloak");
42+
}
43+
}
44+
45+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.baeldung.keycloak;
2+
3+
import org.springframework.context.annotation.Bean;
4+
import org.springframework.context.annotation.Configuration;
5+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
6+
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
7+
import org.springframework.security.core.session.SessionRegistryImpl;
8+
import org.springframework.security.web.SecurityFilterChain;
9+
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
10+
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
11+
12+
@Configuration
13+
@EnableWebSecurity
14+
class SecurityConfig {
15+
16+
private final KeycloakLogoutHandler keycloakLogoutHandler;
17+
18+
SecurityConfig(KeycloakLogoutHandler keycloakLogoutHandler) {
19+
this.keycloakLogoutHandler = keycloakLogoutHandler;
20+
}
21+
22+
@Bean
23+
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
24+
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
25+
}
26+
27+
@Bean
28+
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
29+
http.authorizeRequests()
30+
.antMatchers("/customers*", "/users*")
31+
.hasRole("USER")
32+
.anyRequest()
33+
.permitAll();
34+
http.oauth2Login()
35+
.and()
36+
.logout()
37+
.addLogoutHandler(keycloakLogoutHandler)
38+
.logoutSuccessUrl("/");
39+
return http.build();
40+
}
41+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.baeldung.keycloak;
2+
3+
import org.springframework.boot.SpringApplication;
4+
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
import org.springframework.context.annotation.Bean;
6+
import org.springframework.web.client.RestTemplate;
7+
8+
@SpringBootApplication
9+
10+
public class SpringBoot {
11+
12+
public static void main(String[] args) {
13+
SpringApplication.run(SpringBoot.class, args);
14+
}
15+
16+
@Bean
17+
public RestTemplate restTemplate() {
18+
return new RestTemplate();
19+
}
20+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package com.baeldung.keycloak;
2+
3+
import org.springframework.stereotype.Controller;
4+
import org.springframework.ui.Model;
5+
import org.springframework.web.bind.annotation.GetMapping;
6+
7+
import java.security.Principal;
8+
9+
import org.springframework.beans.factory.annotation.Autowired;
10+
11+
import javax.servlet.http.HttpServletRequest;
12+
13+
@Controller
14+
public class WebController {
15+
16+
@Autowired
17+
private CustomerDAO customerDAO;
18+
19+
@GetMapping(path = "/")
20+
public String index() {
21+
return "external";
22+
}
23+
24+
@GetMapping("/logout")
25+
public String logout(HttpServletRequest request) throws Exception {
26+
request.logout();
27+
return "redirect:/";
28+
}
29+
30+
@GetMapping(path = "/customers")
31+
public String customers(Principal principal, Model model) {
32+
addCustomers();
33+
Iterable<Customer> customers = customerDAO.findAll();
34+
model.addAttribute("customers", customers);
35+
model.addAttribute("username", principal.getName());
36+
return "customers";
37+
}
38+
39+
// add customers for demonstration
40+
public void addCustomers() {
41+
42+
Customer customer1 = new Customer();
43+
customer1.setAddress("1111 foo blvd");
44+
customer1.setName("Foo Industries");
45+
customer1.setServiceRendered("Important services");
46+
customerDAO.save(customer1);
47+
48+
Customer customer2 = new Customer();
49+
customer2.setAddress("2222 bar street");
50+
customer2.setName("Bar LLP");
51+
customer2.setServiceRendered("Important services");
52+
customerDAO.save(customer2);
53+
54+
Customer customer3 = new Customer();
55+
customer3.setAddress("33 main street");
56+
customer3.setName("Big LLC");
57+
customer3.setServiceRendered("Important services");
58+
customerDAO.save(customer3);
59+
}
60+
}

0 commit comments

Comments
 (0)