
Personnalisation de la configuration de sécurité (`WebSecurityConfigurerAdapter` ou `SecurityFilterChain` bean)
Apprenez à personnaliser la configuration de Spring Security dans Spring Boot, de l'ancienne approche WebSecurityConfigurerAdapter (obsolète) à la méthode moderne via les beans SecurityFilterChain.
Au-delà des valeurs par défaut : pourquoi personnaliser Spring Security ?
Spring Boot, via le starter `spring-boot-starter-security`, fournit une configuration de sécurité automatique très pratique pour démarrer rapidement. Elle inclut généralement une protection de base pour toutes les requêtes, un formulaire de connexion généré automatiquement et un utilisateur en mémoire (`user` avec un mot de passe généré). Cependant, cette configuration par défaut est rarement suffisante pour une application réelle.
La personnalisation devient nécessaire pour définir des règles d'accès spécifiques à vos URL (qui peut accéder à quoi ?), intégrer votre propre source d'utilisateurs (base de données, LDAP, OAuth2), configurer un formulaire de connexion personnalisé, gérer la déconnexion, ajuster la protection CSRF, ou configurer des aspects plus avancés comme la gestion de session ou les en-têtes de sécurité.
Historiquement, cette personnalisation se faisait principalement en étendant la classe `WebSecurityConfigurerAdapter`. Toutefois, cette approche est désormais obsolète au profit d'une configuration basée sur les composants (beans Spring), jugée plus flexible et modulaire.
L'approche historique (obsolète) : `WebSecurityConfigurerAdapter`
Pendant longtemps, la méthode standard pour personnaliser la sécurité web consistait à créer une classe de configuration (annotée avec `@Configuration` et `@EnableWebSecurity`) qui étendait `WebSecurityConfigurerAdapter`. Cette classe abstraite fournissait des méthodes à surcharger pour configurer différents aspects de la sécurité.
La méthode la plus couramment surchargée était `configure(HttpSecurity http)`. A l'intérieur de cette méthode, on utilisait l'objet `HttpSecurity` pour chaîner des appels de configuration définissant les règles d'autorisation, le type d'authentification (formulaire, HTTP Basic), la gestion de la déconnexion, la protection CSRF, etc.
Exemple conceptuel (NE PAS UTILISER DANS LES NOUVEAUX PROJETS) :
@Configuration
@EnableWebSecurity
@Deprecated // Cette approche est obsolète
public class OldSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/public/**", "/login").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/home")
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login?logout");
// ... autres configurations
}
// On pouvait aussi surcharger configure(AuthenticationManagerBuilder auth)
// pour configurer l'authentification (UserDetailsService, PasswordEncoder)
// et configure(WebSecurity web) pour ignorer certaines requêtes.
}Bien que vous puissiez encore rencontrer cette syntaxe dans des projets plus anciens, il est fortement recommandé d'adopter la nouvelle approche basée sur les beans `SecurityFilterChain` pour tout nouveau développement ou lors de la mise à jour de dépendances.
L'approche moderne : les beans `SecurityFilterChain`
Depuis Spring Security 5.7, la manière privilégiée de configurer la sécurité web est de définir un ou plusieurs beans de type `SecurityFilterChain` dans une classe de configuration standard (annotée `@Configuration` et, si l'auto-configuration n'est pas suffisante, `@EnableWebSecurity`). Cette approche orientée composants favorise une meilleure séparation des préoccupations et s'intègre plus naturellement avec l'injection de dépendances de Spring.
Au lieu de surcharger une méthode, vous déclarez une méthode annotée avec `@Bean` qui retourne une instance de `SecurityFilterChain`. Cette méthode prend en paramètre un objet `HttpSecurity` que vous utilisez pour configurer la chaîne de filtres de sécurité, de manière très similaire à l'ancienne méthode `configure`, mais en utilisant souvent le style de programmation fonctionnelle avec des lambdas (DSL Lambda).
Structure de base :
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import static org.springframework.security.config.Customizer.withDefaults;
@Configuration
@EnableWebSecurity // Souvent implicite avec Spring Boot si @Configuration est présente
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// Configuration de la chaîne de filtres ici
http
// ... configuration ...
;
return http.build();
}
// D'autres beans peuvent être définis ici (PasswordEncoder, UserDetailsService, etc.)
}Configuration détaillée avec `HttpSecurity` et le DSL Lambda
L'objet `HttpSecurity` reste central pour la configuration. Le DSL Lambda rend la configuration plus concise et lisible. Voici comment configurer les aspects courants :
1. Autorisation des requêtes (`authorizeHttpRequests`) : Définit quelles requêtes nécessitent quelle autorisation.
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/css/**", "/js/**", "/images/**", "/public/**", "/login").permitAll() // Autorise l'accès sans authentification
.requestMatchers("/api/admin/**").hasRole("ADMIN") // Nécessite le rôle ADMIN
.requestMatchers("/api/users/**").hasAnyRole("USER", "ADMIN") // Rôle USER ou ADMIN
.requestMatchers("/sensitive-data").hasAuthority("READ_SENSITIVE") // Nécessite une autorité spécifique
.anyRequest().authenticated() // Toutes les autres requêtes nécessitent une authentification
);
// ... suite de la configuration2. Authentification par formulaire (`formLogin`) : Configure l'authentification basée sur un formulaire HTML.
http
.formLogin(formLogin -> formLogin
.loginPage("/login") // Page de connexion personnalisée (doit être permitAll() dans authorizeHttpRequests)
.loginProcessingUrl("/perform_login") // URL où le formulaire est soumis (POST)
.defaultSuccessUrl("/home", true) // Redirection après succès
.failureUrl("/login?error=true") // Redirection après échec
.permitAll() // Important: autoriser l'accès à loginPage et failureUrl
);
// ... suite de la configurationSi vous omettez `.loginPage()`, Spring Security génère une page par défaut.
3. Authentification HTTP Basic (`httpBasic`) : Active l'authentification HTTP Basic.
http
.httpBasic(withDefaults()); // Active HTTP Basic avec les paramètres par défaut
// ... suite de la configuration4. Déconnexion (`logout`) : Configure le processus de déconnexion.
http
.logout(logout -> logout
.logoutUrl("/perform_logout") // URL pour déclencher la déconnexion (POST par défaut pour CSRF)
.logoutSuccessUrl("/login?logout") // Redirection après déconnexion
.invalidateHttpSession(true) // Invalider la session HTTP
.deleteCookies("JSESSIONID") // Supprimer les cookies spécifiés
.permitAll()
);
// ... suite de la configuration5. Protection CSRF (`csrf`) : Configure la protection Cross-Site Request Forgery.
http
// CSRF est activé par défaut pour les applications web traditionnelles.
// Pour le désactiver (par exemple pour une API REST stateless) :
// .csrf(csrf -> csrf.disable())
// Pour personnaliser (par exemple, avec un CsrfTokenRepository personnalisé) :
.csrf(csrf -> csrf
// .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
// .csrfTokenRequestHandler(new CsrfTokenRequestAttributeNameRequestHandler())
// ... autres options
);
// ... suite de la configurationN'oubliez pas de terminer la configuration de l'objet `HttpSecurity` par `.build()` pour retourner l'instance `SecurityFilterChain`.
Gestion de plusieurs chaînes de filtres (`SecurityFilterChain`)
L'approche basée sur les beans permet de définir facilement plusieurs `SecurityFilterChain`. Ceci est utile pour appliquer des configurations de sécurité radicalement différentes à différentes parties de votre application, par exemple, une chaîne pour votre API REST stateless (souvent avec CSRF désactivé et authentification par token) et une autre pour votre interface utilisateur web traditionnelle (avec formulaire de connexion et protection CSRF).
Spring Security sélectionne la première chaîne de filtres dont le `requestMatchers` correspond à la requête entrante. L'ordre est donc important. Vous pouvez influencer l'ordre des beans `SecurityFilterChain` en utilisant l'annotation `@Order(value)` sur les méthodes `@Bean`. Les valeurs inférieures ont une priorité plus élevée. Souvent, Spring Boot parvient à déterminer un ordre raisonnable basé sur la spécificité des `requestMatchers`.
Exemple avec deux chaînes :
@Configuration
@EnableWebSecurity
public class MultiChainSecurityConfig {
@Bean
@Order(1) // Priorité plus élevée pour les URLs d'API
public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {
http
.securityMatcher("/api/**") // Appliquer cette chaîne uniquement aux URLs commençant par /api/
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/api/public/**").permitAll()
.anyRequest().authenticated()
)
.httpBasic(withDefaults()) // Utiliser HTTP Basic pour l'API
.csrf(csrf -> csrf.disable()); // Désactiver CSRF pour l'API stateless
return http.build();
}
@Bean
@Order(2) // Priorité moins élevée pour le reste
public SecurityFilterChain formLoginFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/css/**", "/js/**", "/login").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.formLogin(formLogin -> formLogin
.loginPage("/login").permitAll()
)
.logout(logout -> logout.permitAll());
// CSRF est activé par défaut ici
return http.build();
}
}Le `securityMatcher` (ou `requestMatchers` au début de la configuration) est essentiel pour indiquer à Spring Security quelles requêtes doivent être traitées par quelle chaîne.