
Authentification basée sur un formulaire (Form Login)
Apprenez à configurer et personnaliser l'authentification basée sur un formulaire (Form Login) dans Spring Boot avec Spring Security, incluant page de connexion, gestion des succès/échecs et intégration UserDetailsService.
Qu'est-ce que l'authentification par formulaire ?
L'authentification basée sur un formulaire, communément appelée "Form Login", est sans doute la méthode la plus répandue pour sécuriser les applications web traditionnelles où un utilisateur interagit via un navigateur. Le principe est simple : lorsqu'un utilisateur non authentifié tente d'accéder à une ressource protégée, il est redirigé vers une page HTML contenant un formulaire. Ce formulaire lui demande généralement de saisir un nom d'utilisateur et un mot de passe.
Une fois le formulaire soumis (typiquement via une requête HTTP POST), le serveur vérifie les informations d'identification fournies. Si elles sont valides, l'utilisateur est considéré comme authentifié, une session est établie (ou mise à jour), et il est généralement redirigé vers la page qu'il tentait initialement d'accéder ou vers une page d'accueil par défaut. En cas d'échec, il est habituellement redirigé vers la page de connexion avec un message d'erreur.
Spring Security offre un support robuste et hautement configurable pour ce mécanisme d'authentification, simplifiant considérablement sa mise en oeuvre pour les développeurs.
Activation et comportement par défaut dans Spring Security
Activer l'authentification par formulaire de base dans Spring Security (en utilisant la configuration basée sur les beans `SecurityFilterChain`) est très simple. Il suffit d'appeler la méthode `formLogin()` sur l'objet `HttpSecurity`.
Si vous utilisez les paramètres par défaut via `http.formLogin(withDefaults());` ou même simplement `http.formLogin();` (qui applique aussi des valeurs par défaut), Spring Security met en place les éléments suivants :
- Une page de connexion générée automatiquement à l'URL `/login`. Si un utilisateur non authentifié accède à une page protégée, il sera redirigé vers cette page.
- Cette page de connexion contient un formulaire demandant un `username` et un `password`.
- Le formulaire doit être soumis via une requête `POST` à l'URL `/login`. C'est l'URL que le filtre de Spring Security écoute par défaut pour traiter les tentatives de connexion.
- Une page d'erreur générée automatiquement est affichée si l'authentification échoue (à l'URL `/login?error`).
- La fonctionnalité de déconnexion est également activée par défaut (accessible via `POST` à `/logout`), redirigeant vers `/login?logout` après succès.
- La protection CSRF est activée, ce qui signifie que le formulaire de connexion généré inclura un champ caché pour le token CSRF.
Ce comportement par défaut est très utile pour démarrer rapidement ou pour des applications simples, mais la plupart des applications réelles nécessiteront une personnalisation.
Personnalisation du Form Login via `HttpSecurity`
La véritable puissance de Spring Security réside dans sa capacité de personnalisation. En utilisant le DSL Lambda avec `formLogin()`, vous pouvez finement contrôler chaque aspect du processus d'authentification par formulaire au sein de votre bean `SecurityFilterChain`.
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
// Important : Autoriser l'accès aux pages de login/erreur
.requestMatchers("/login", "/login-error").permitAll()
.requestMatchers("/css/**", "/js/**").permitAll()
.anyRequest().authenticated()
)
.formLogin(formLogin -> formLogin
// 1. Spécifier une page de connexion personnalisée
.loginPage("/login")
// 2. URL où le formulaire POSTe les données (interceptée par Spring Security)
.loginProcessingUrl("/perform_login")
// 3. URL de redirection après succès (true force cette URL même si une page était visée)
.defaultSuccessUrl("/dashboard", true)
// 4. URL de redirection en cas d'échec
.failureUrl("/login-error")
// 5. Noms des paramètres dans le formulaire HTML (si différents de "username" et "password")
.usernameParameter("email")
.passwordParameter("passphrase")
// 6. Permettre à tous d'accéder à la page de connexion et à la page d'erreur
// (Déjà fait via permitAll() dans authorizeHttpRequests mais redondant ici par clarté)
.permitAll()
);
// ... autres configurations (logout, csrf, etc.)
return http.build();
}Points clés à retenir de cet exemple :
- `loginPage("/login")`: Définit l'URL de votre page de connexion personnalisée. Spring Security redirigera vers cette URL au lieu de générer la sienne. Vous devez créer le contrôleur et la vue correspondants.
- `loginProcessingUrl("/perform_login")`: C'est l'URL cible de l'attribut `action` de votre formulaire HTML de connexion. Vous n'avez PAS besoin de créer un contrôleur pour cette URL; le filtre `UsernamePasswordAuthenticationFilter` de Spring Security l'interceptera automatiquement.
- `defaultSuccessUrl("/dashboard", true)`: Après une connexion réussie, l'utilisateur est redirigé ici. Le second paramètre `true` force cette redirection même si l'utilisateur tentait d'accéder à une autre page protégée avant d'être redirigé vers le login. Mettre `false` (ou omettre) redirigerait vers la page initialement demandée si elle existe.
- `failureUrl("/login-error")`: URL vers laquelle rediriger si l'authentification échoue. Vous devez créer un contrôleur et une vue pour cette page afin d'afficher un message d'erreur.
- `usernameParameter("email")` / `passwordParameter("passphrase")`: Permet d'utiliser des noms différents pour les champs `input` dans votre formulaire HTML.
- `permitAll()` pour les pages de login/erreur: Il est crucial d'autoriser l'accès à votre `loginPage` et `failureUrl` dans la section `authorizeHttpRequests`, sinon les utilisateurs non authentifiés ne pourront même pas afficher le formulaire de connexion !
Création de la page de connexion personnalisée
Lorsque vous spécifiez une `loginPage` personnalisée, vous devez fournir le contenu HTML correspondant. Ce formulaire doit respecter certaines conventions pour fonctionner avec Spring Security :
- L'attribut `action` de la balise `
- La méthode HTTP doit être `POST`.
- Il doit contenir des champs `input` dont les attributs `name` correspondent à ceux attendus par Spring Security (par défaut `username` et `password`, ou ceux définis par `usernameParameter` et `passwordParameter`).
- Très important : Si la protection CSRF est activée (ce qui est le cas par défaut), le formulaire DOIT inclure un champ caché contenant le token CSRF. Avec Thymeleaf, c'est facile :
Exemple simple de formulaire de connexion (avec Thymeleaf) :
Connexion
Veuillez vous connecter
Identifiants invalides.
Vous avez été déconnecté.
Vous aurez également besoin d'un contrôleur Spring MVC simple pour afficher cette page lorsque l'URL `/login` est accédée via GET.
Intégration avec `UserDetailsService` et `PasswordEncoder`
L'authentification par formulaire repose sur deux composants essentiels que vous devez généralement fournir en tant que beans Spring :
- `UserDetailsService`: Une interface avec une seule méthode `loadUserByUsername(String username)`. Son rôle est de charger les détails de l'utilisateur (y compris le mot de passe haché et les autorités/rôles) à partir de votre source de données (base de données, LDAP, etc.) en fonction du nom d'utilisateur fourni.
- `PasswordEncoder`: Une interface utilisée pour hacher les mots de passe lors de leur stockage et pour comparer le mot de passe fourni par l'utilisateur lors de la connexion avec le hachage stocké. L'implémentation recommandée est `BCryptPasswordEncoder`.
Spring Security détecte automatiquement les beans de ces types dans le contexte et les utilise lors du traitement de l'authentification par formulaire via l'`AuthenticationManager`.
@Configuration
public class SecurityBeansConfig {
// Bean pour charger les utilisateurs (ici, un exemple simple en mémoire)
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.builder()
.username("user")
.password(passwordEncoder().encode("password")) // Le mot de passe DOIT être encodé
.roles("USER")
.build();
UserDetails admin = User.builder()
.username("admin")
.password(passwordEncoder().encode("admin"))
.roles("ADMIN", "USER")
.build();
return new InMemoryUserDetailsManager(user, admin);
// Dans une vraie application, vous implémenteriez ce service
// pour charger les utilisateurs depuis une base de données via JPA/JDBC.
}
// Bean essentiel pour l'encodage et la vérification des mots de passe
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}Avec ces beans définis, lorsque l'utilisateur soumet le formulaire de connexion, Spring Security utilisera votre `UserDetailsService` pour trouver l'utilisateur et votre `PasswordEncoder` pour vérifier le mot de passe.