12- TP 12- Keycloak
12- TP 12- Keycloak
12- TP 12- Keycloak
I. Objectif du TP ................................................................................................................................................... 2
b. La classe DTO..............................................................................................................................................11
e. Le contrôlleur .............................................................................................................................................13
f. La classe de démarrage..............................................................................................................................14
h. La classe de sécurité...................................................................................................................................16
Conclusion ..............................................................................................................................................................21
II. Prérequis
- IntelliJ IDEA ;
- JDK version 17 ;
- Une connexion Internet pour permettre à Maven de télécharger les librairies.
- Vérifier ensuite que le serveur Keycoak est bien démarré (par défait Keycloak utilise le port 8080) :
- Dans la console d’administration, cliquer sur la liste déroulante master et cliquer sur le bouton
« Create Realm » comme expliqué ci-dessous :
- Cliquer sur Create client et créer le Client « login-app » comme expliqué ci-dessous :
- Par la suite, laisser les paramètres par défaut sauf pour le champ Valid Redirect URIs :
- Cliquer ensuite sur le bouton « Create role » pour créer un nouveau rôle (par exemple le rôle USER) :
- Cliquer ensuite sur l’onglet Role mapping pour assigner le rôle USER à l’utilisateur user1 :
- Au niveau du corps de la requête il faut envoyer les données suivantes dans le format x-www-form-
urlencoded :
a. Le fichier pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
b. La classe DTO
package ma.formations.dtos;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
public class CustomerDto {
private Long id;
private String name;
private String serviceRendered;
private String address;
}
c. La couche service
package ma.formations.service.model;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Builder;
Authentification avec Keycloak 11
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Data
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String serviceRendered;
private String address;
}
package ma.formations.service;
import ma.formations.dtos.CustomerDto;
import java.util.List;
List<CustomerDto> getAllCustomers();
}
package ma.formations.service;
import lombok.AllArgsConstructor;
import ma.formations.dao.CustomerRepository;
import ma.formations.dtos.CustomerDto;
import ma.formations.service.model.Customer;
import org.modelmapper.ModelMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Transactional
@Service
@AllArgsConstructor
public class ServiceImpl implements IService {
private CustomerRepository customerRepository;
private ModelMapper modelMapper;
@Override
public void save(CustomerDto dto) {
customerRepository.save(modelMapper.map(dto, Customer.class));
}
d. La couche DAO
package ma.formations.dao;
import ma.formations.service.model.Customer;
import org.springframework.data.jpa.repository.JpaRepository;
e. Le contrôlleur
package ma.formations.presentation;
import jakarta.servlet.http.HttpServletRequest;
import lombok.AllArgsConstructor;
import ma.formations.service.IService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import java.security.Principal;
@Controller
@AllArgsConstructor
@GetMapping(path = "/")
public String index() {
return "external";
}
@GetMapping("/logout")
public String logout(HttpServletRequest request) throws Exception {
request.logout();
return "redirect:/";
}
@GetMapping(path = "/customers")
f. La classe de démarrage
import ma.formations.dtos.CustomerDto;
import ma.formations.service.IService;
import org.modelmapper.ModelMapper;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class MainApplication {
@Bean
public ModelMapper modelMapper() {
return new ModelMapper();
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean
public CommandLineRunner initDatabase(IService customerService) {
customerService.save(CustomerDto.builder().
address("1111 foo blvd").
name("Foo Industries").
serviceRendered("Important services").
build());
customerService.save(CustomerDto.builder().
address("2222 bar street").
name("Bar LLP").
serviceRendered("Important services").
build());
customerService.save(CustomerDto.builder().
address("33 main street").
name("Big LLC").
serviceRendered("Important services").
g. La classe du handler
package ma.formations.handler;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
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;
@Component
public class KeycloakLogoutHandler implements LogoutHandler {
@Override
public void logout(HttpServletRequest request, HttpServletResponse response,
Authentication auth) {
logoutFromKeycloak((OidcUser) auth.getPrincipal());
}
ResponseEntity<String> 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");
}
}
import ma.formations.handler.KeycloakLogoutHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
import
org.springframework.security.config.annotation.authentication.builders.Authenticati
onManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import
org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.web.SecurityFilterChain;
import
org.springframework.security.web.authentication.session.RegisterSessionAuthenticati
onStrategy;
import
org.springframework.security.web.authentication.session.SessionAuthenticationStrate
gy;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final KeycloakLogoutHandler keycloakLogoutHandler;
SecurityConfig(KeycloakLogoutHandler keycloakLogoutHandler) {
this.keycloakLogoutHandler = keycloakLogoutHandler;
}
@Bean
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new
SessionRegistryImpl());
}
@Order(1)
@Bean
public SecurityFilterChain clientFilterChain(HttpSecurity http) throws
Exception {
return http.authorizeHttpRequests(auth -> {
auth.requestMatchers("/")
.permitAll()
.anyRequest()
.authenticated();
}).oauth2Login(Customizer.withDefaults()).
logout(logout -> logout
.logoutSuccessUrl("/")
.addLogoutHandler(keycloakLogoutHandler)
).build();
@Order(2)
@Bean
public AuthenticationManager authenticationManager(HttpSecurity http) throws
Exception {
return http.getSharedObject(AuthenticationManagerBuilder.class)
.build();
}
}
i. Le fichier application.properties
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:8080/realms/springbootKeycloak
spring.security.oauth2.client.provider.keycloak.user-name-
attribute=preferred_username
spring.security.oauth2.resourceserver.jwt.issuer-
uri=http://localhost:8080/realms/springbootKeycloak
server.port=8081
# The name of the H2 database :
spring.datasource.url=jdbc:h2:mem:testdb
# The H2 Driver :
spring.datasource.driverClassName=org.h2.Driver
spring.data.jpa.repositories.bootstrap-mode=default
spring.datasource.username=sa
spring.datasource.password=
# automatic creation and modification of tables
spring.jpa.hibernate.ddl-auto=update
# Activate the H2 console :
spring.h2.console.enabled=true
# For customizing the console URL
spring.h2.console.path=/h2
<head th:fragment="headerFragment">
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
<title>Customer Portal</title>
<link
crossorigin="anonymous"
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
integrity="sha384-
BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
rel="stylesheet"></link>
<link
href="https://cdn.datatables.net/1.10.16/css/jquery.dataTables.min.css"
rel="stylesheet"></link>
</head>
La page external.xml :
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head th:include="layout :: headerFragment">
</head>
<body>
<div class="container">
<div class="jumbotron text-center">
<h1>Customer Portal</h1>
</div>
<div>
<p>VOTRE PAGE POUR LE PUBLIC</p>
<h2>Existing Customers</h2>
<div class="well">
<b>Enter the intranet: </b><a th:href="@{/customers}">customers</a>
</div>
</div>
<div id="pagefoot" th:include="layout :: footerFragment">Footer
</div>
</div>
<!-- container -->
</body>
</html>
La page customers.xml :
- Entrer votre compte (par exemple username=user1 et password=user1). Ici c’est le compte utilisateur
que vous avez ajouté au niveau de Keycloak.
Conclusion
Le code source de cet atelier est disponible sur GITHUB :
https://github.com/abbouformations/spring-security-oauth2-keyclock.git