In un’applicazione reale è raro usare la pagina di login generata automaticamente da Spring Security. Quella pagina è molto utile per testare rapidamente la configurazione, ma in un progetto professionale quasi sempre serve una maschera di login personalizzata, coerente con il design della web application.

Una maschera di login personalizzata in Spring Security permette di controllare HTML, CSS, messaggi di errore, layout, branding, comportamento dopo il login, gestione del logout e protezione CSRF.

Spring Security offre un meccanismo molto flessibile per personalizzare il form login. Possiamo indicare una pagina di login custom, configurare l’URL a cui inviare username e password, definire la pagina di destinazione dopo l’autenticazione, gestire errori di login e impostare il logout.

In breve: per creare un login personalizzato in Spring Security bisogna configurare formLogin(), permettere l’accesso pubblico alla pagina di login e alle risorse statiche, inviare il form con metodo POST a /login e includere il token CSRF.

Indice dei contenuti

Cos’è una maschera di login personalizzata

Una maschera di login personalizzata è una pagina creata dallo sviluppatore per raccogliere le credenziali dell’utente e inviarle a Spring Security per l’autenticazione.

La pagina può essere realizzata con:

  • Thymeleaf;
  • JSP;
  • HTML statico servito dall’applicazione;
  • template engine alternativi;
  • frontend separato, per esempio React, Angular o Vue;
  • una pagina generata dal backend Spring MVC.

Il punto importante è che il form deve rispettare alcune regole attese da Spring Security. In particolare, deve inviare una richiesta POST all’URL configurato per il login, includere i parametri delle credenziali e, se la protezione CSRF è attiva, includere anche il token CSRF.

Login generato automaticamente vs login personalizzato

Tipo di login Vantaggi Limiti
Login generato da Spring Security Subito disponibile, utile per test e prototipi. Design generico, poco adatto a progetti reali.
Login personalizzato Controllo completo su HTML, CSS, messaggi e UX. Richiede configurazione corretta di URL, CSRF e permessi.

Come funziona il form login in Spring Security

Il form login di Spring Security si basa su un flusso abbastanza semplice:

Utente apre una pagina protetta
   ↓
Spring Security intercetta la richiesta
   ↓
L’utente viene reindirizzato alla pagina di login
   ↓
L’utente invia username e password
   ↓
Spring Security autentica le credenziali
   ↓
Login corretto → redirect alla pagina protetta
Login fallito → redirect alla pagina di login con errore

Il form di login non deve autenticare direttamente l’utente nel codice HTML. Deve semplicemente inviare le credenziali all’endpoint gestito da Spring Security.

Per impostazione comune, Spring Security si aspetta:

  • una richiesta POST verso /login;
  • un parametro username;
  • un parametro password;
  • un token CSRF se la protezione è attiva;
  • eventuale parametro error per mostrare un messaggio di login fallito;
  • eventuale parametro logout per mostrare un messaggio di logout completato.

Concetto chiave: il form personalizzato è tuo, ma l’elaborazione dell’autenticazione resta gestita da Spring Security.

Struttura del progetto

Una struttura semplice per un progetto Spring Boot con login personalizzato può essere questa:

src
 └── main
     ├── java
     │   └── it.javaboss.security
     │       ├── SecurityConfig.java
     │       ├── HomeController.java
     │       └── UserController.java
     └── resources
         ├── templates
         │   ├── login.html
         │   └── index.html
         ├── static
         │   ├── css
         │   │   └── style.css
         │   └── images
         └── application.properties

In questo esempio:

  • SecurityConfig.java contiene la configurazione di Spring Security;
  • login.html contiene la pagina di login;
  • index.html è una pagina protetta;
  • static/css/style.css contiene il CSS della maschera;
  • le risorse statiche devono essere accessibili anche agli utenti non autenticati.

Dipendenze Maven

In un progetto Spring Boot moderno, le dipendenze principali sono:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity6</artifactId>
</dependency>

Se usi JSP, la configurazione cambia e può richiedere dipendenze e setup specifici per il servlet container. Per i nuovi progetti Spring Boot, Thymeleaf è spesso più semplice e naturale.

Configurazione moderna con SecurityFilterChain

Nelle versioni moderne di Spring Security si usa un bean SecurityFilterChain per configurare la sicurezza HTTP.

Un esempio base di configurazione per login personalizzato è il seguente:

package it.javaboss.security;

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.web.SecurityFilterChain;

@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/login", "/css/**", "/images/**", "/js/**").permitAll()
                        .anyRequest().authenticated()
                )
                .formLogin(form -> form
                        .loginPage("/login")
                        .loginProcessingUrl("/login")
                        .defaultSuccessUrl("/", true)
                        .failureUrl("/login?error=true")
                        .permitAll()
                )
                .logout(logout -> logout
                        .logoutUrl("/logout")
                        .logoutSuccessUrl("/login?logout=true")
                        .invalidateHttpSession(true)
                        .deleteCookies("JSESSIONID")
                        .permitAll()
                );

        return http.build();
    }
}

Questa configurazione fa diverse cose:

  • permette a tutti di accedere alla pagina /login;
  • permette a tutti di scaricare CSS, JavaScript e immagini;
  • richiede autenticazione per tutte le altre richieste;
  • imposta una pagina di login personalizzata;
  • configura l’URL che processa il login;
  • configura il redirect in caso di login corretto;
  • configura il redirect in caso di errore;
  • configura il logout.

Nota moderna: nelle versioni attuali di Spring Security non è più consigliato basarsi su WebSecurityConfigurerAdapter. È preferibile dichiarare un bean SecurityFilterChain.

Configurare una pagina di login custom

Il punto centrale della configurazione è questo blocco:

.formLogin(form -> form
        .loginPage("/login")
        .loginProcessingUrl("/login")
        .defaultSuccessUrl("/", true)
        .failureUrl("/login?error=true")
        .permitAll()
)

Vediamo il significato dei metodi principali.

Metodo Significato
loginPage("/login") Indica la pagina che mostra il form di login.
loginProcessingUrl("/login") Indica l’URL a cui il form deve inviare username e password.
defaultSuccessUrl("/", true) Indica dove reindirizzare l’utente dopo login corretto.
failureUrl("/login?error=true") Indica dove reindirizzare l’utente in caso di login fallito.
permitAll() Permette a tutti di accedere agli endpoint necessari al login.

Controller per mostrare la pagina di login

package it.javaboss.security;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class LoginController {

    @GetMapping("/login")
    public String login() {
        return "login";
    }
}

Con Thymeleaf, questo metodo cerca il template:

src/main/resources/templates/login.html

HTML della pagina di login

La pagina di login deve contenere un form che invia una richiesta POST all’URL configurato con loginProcessingUrl.

Template login.html con Thymeleaf

<!DOCTYPE html>
<html lang="it" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Login</title>
    <link rel="stylesheet" th:href="@{/css/style.css}">
</head>
<body>

    <main>
        <section>
            <h1>Login</h1>

            <div th:if="${param.error}">
                <p>Username o password non validi.</p>
            </div>

            <div th:if="${param.logout}">
                <p>Logout eseguito correttamente.</p>
            </div>

            <form th:action="@{/login}" method="post">
                <div>
                    <label for="username">Username</label>
                    <input type="text" id="username" name="username" autocomplete="username" required>
                </div>

                <div>
                    <label for="password">Password</label>
                    <input type="password" id="password" name="password" autocomplete="current-password" required>
                </div>

                <input type="hidden"
                       th:name="${_csrf.parameterName}"
                       th:value="${_csrf.token}">

                <button type="submit">Accedi</button>
            </form>
        </section>
    </main>

</body>
</html>

Gli elementi più importanti sono:

  • method="post";
  • th:action="@{/login}";
  • name="username";
  • name="password";
  • campo hidden con token CSRF.

Attenzione: se il form non invia i parametri con i nomi attesi, Spring Security non riuscirà a leggere username e password.

Token CSRF nel form di login

Spring Security abilita normalmente la protezione CSRF per le applicazioni web basate su sessione. Questo significa che le richieste modificative, come il login via POST o il logout via POST, devono includere un token valido.

In Thymeleaf possiamo includere il token così:

<input type="hidden"
       th:name="${_csrf.parameterName}"
       th:value="${_csrf.token}">

In JSP, invece, si può usare una sintassi simile:

<input type="hidden"
       name="${_csrf.parameterName}"
       value="${_csrf.token}" />

Cosa succede se manca il token CSRF?

Se il token CSRF manca o non è valido, Spring Security può bloccare la richiesta e restituire un errore, spesso un 403 Forbidden.

HTTP 403 Forbidden

Per questo motivo, quando crei una pagina di login personalizzata, devi sempre verificare che il token CSRF venga incluso correttamente nel form.

Best practice: non disabilitare CSRF solo per far funzionare il form. Prima controlla che il token sia presente e venga inviato correttamente.

Gestione degli errori di autenticazione

Quando l’autenticazione fallisce, possiamo reindirizzare l’utente alla pagina di login con un parametro di errore.

.failureUrl("/login?error=true")

Nel template, possiamo mostrare un messaggio se il parametro error è presente.

Con Thymeleaf

<div th:if="${param.error}">
    <p>Username o password non validi.</p>
</div>

Con JSP

<c:if test="${param.error != null}">
    <div>
        <p>Username o password non validi.</p>
    </div>
</c:if>

È meglio usare messaggi generici, senza specificare se il problema è lo username o la password. Questo evita di dare informazioni utili a un eventuale attaccante.

Sicurezza: preferisci messaggi come “Credenziali non valide” invece di “Username inesistente” o “Password errata”.

Parametri username e password

Per impostazione predefinita, Spring Security si aspetta che il form invii:

  • username per il nome utente;
  • password per la password.
<input type="text" name="username">
<input type="password" name="password">

Se vuoi usare nomi diversi, puoi configurarli in Spring Security.

Parametri personalizzati

.formLogin(form -> form
        .loginPage("/login")
        .loginProcessingUrl("/login")
        .usernameParameter("email")
        .passwordParameter("secret")
        .defaultSuccessUrl("/", true)
        .failureUrl("/login?error=true")
)

In questo caso il form dovrà usare:

<input type="text" name="email">
<input type="password" name="secret">

Questa personalizzazione può essere utile quando l’utente effettua il login con email invece che con username.

Configurazione del logout

Il logout è un’altra parte importante della configurazione. Un esempio moderno è:

.logout(logout -> logout
        .logoutUrl("/logout")
        .logoutSuccessUrl("/login?logout=true")
        .invalidateHttpSession(true)
        .deleteCookies("JSESSIONID")
        .permitAll()
)

Questa configurazione indica che:

  • il logout avviene tramite /logout;
  • dopo il logout l’utente viene reindirizzato a /login?logout=true;
  • la sessione HTTP viene invalidata;
  • il cookie JSESSIONID viene eliminato;
  • l’endpoint di logout è accessibile secondo la configurazione di sicurezza.

Form di logout con CSRF

Con CSRF attivo, il logout dovrebbe essere eseguito tramite form POST.

<form th:action="@{/logout}" method="post">
    <input type="hidden"
           th:name="${_csrf.parameterName}"
           th:value="${_csrf.token}">

    <button type="submit">Logout</button>
</form>

Messaggio di logout completato

<div th:if="${param.logout}">
    <p>Logout eseguito correttamente.</p>
</div>

Nota importante: se CSRF è attivo, un semplice link <a href="/logout"> può non essere sufficiente o non essere la scelta corretta. È preferibile usare una form POST con token CSRF.

Homepage protetta dopo il login

Dopo il login, possiamo mostrare una pagina protetta accessibile solo agli utenti autenticati.

Controller home

package it.javaboss.security;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HomeController {

    @GetMapping("/")
    public String home() {
        return "index";
    }
}

Template index.html con Thymeleaf

<!DOCTYPE html>
<html lang="it"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="https://www.thymeleaf.org/extras/spring-security">
<head>
    <meta charset="UTF-8">
    <title>Home</title>
</head>
<body>

    <h1>Area riservata</h1>

    <p>
        Benvenuto:
        <strong sec:authentication="name">utente</strong>
    </p>

    <form th:action="@{/logout}" method="post">
        <input type="hidden"
               th:name="${_csrf.parameterName}"
               th:value="${_csrf.token}">

        <button type="submit">Logout</button>
    </form>

</body>
</html>

La pagina mostra il nome dell’utente autenticato e una form per eseguire il logout.

Esempio completo con Thymeleaf

Riepiloghiamo una configurazione moderna completa con Spring Boot, Spring Security e Thymeleaf.

SecurityConfig.java

package it.javaboss.security;

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.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/login", "/css/**", "/js/**", "/images/**").permitAll()
                        .anyRequest().authenticated()
                )
                .formLogin(form -> form
                        .loginPage("/login")
                        .loginProcessingUrl("/login")
                        .defaultSuccessUrl("/", true)
                        .failureUrl("/login?error=true")
                        .permitAll()
                )
                .logout(logout -> logout
                        .logoutUrl("/logout")
                        .logoutSuccessUrl("/login?logout=true")
                        .invalidateHttpSession(true)
                        .deleteCookies("JSESSIONID")
                        .permitAll()
                );

        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService(PasswordEncoder passwordEncoder) {
        UserDetails user = User.builder()
                .username("mario")
                .password(passwordEncoder.encode("password"))
                .roles("USER")
                .build();

        return new InMemoryUserDetailsManager(user);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

LoginController.java

package it.javaboss.security;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class LoginController {

    @GetMapping("/login")
    public String login() {
        return "login";
    }
}

HomeController.java

package it.javaboss.security;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HomeController {

    @GetMapping("/")
    public String index() {
        return "index";
    }
}

login.html

<!DOCTYPE html>
<html lang="it" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Login</title>
</head>
<body>

    <h1>Login</h1>

    <div th:if="${param.error}">
        <p>Credenziali non valide.</p>
    </div>

    <div th:if="${param.logout}">
        <p>Logout eseguito correttamente.</p>
    </div>

    <form th:action="@{/login}" method="post">
        <div>
            <label for="username">Username</label>
            <input type="text" id="username" name="username" required autofocus>
        </div>

        <div>
            <label for="password">Password</label>
            <input type="password" id="password" name="password" required>
        </div>

        <input type="hidden"
               th:name="${_csrf.parameterName}"
               th:value="${_csrf.token}">

        <button type="submit">Accedi</button>
    </form>

</body>
</html>

index.html

<!DOCTYPE html>
<html lang="it"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="https://www.thymeleaf.org/extras/spring-security">
<head>
    <meta charset="UTF-8">
    <title>Home</title>
</head>
<body>

    <h1>Homepage protetta</h1>

    <p>
        Utente autenticato:
        <strong sec:authentication="name">utente</strong>
    </p>

    <form th:action="@{/logout}" method="post">
        <input type="hidden"
               th:name="${_csrf.parameterName}"
               th:value="${_csrf.token}">

        <button type="submit">Logout</button>
    </form>

</body>
</html>

Esempio legacy con JSP

In molti progetti legacy, la maschera di login è realizzata con JSP. Anche in questo caso, i concetti sono gli stessi: form POST, parametri corretti, gestione dell’errore e token CSRF.

login.jsp

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<html>
<head>
    <title>Login</title>
    <link rel="stylesheet" href="${pageContext.request.contextPath}/css/style.css" />
</head>
<body onload="document.f.username.focus();">

    <c:if test="${param.error != null}">
        <div>
            <p>Credenziali non valide.</p>
        </div>
    </c:if>

    <c:if test="${param.logout != null}">
        <div>
            <p>Logout eseguito correttamente.</p>
        </div>
    </c:if>

    <form name="f" method="POST" action="${pageContext.request.contextPath}/login">
        <div>
            <h1>Login</h1>

            <label for="username">Username</label>
            <input type="text" id="username" name="username" />

            <label for="password">Password</label>
            <input type="password" id="password" name="password" />

            <input type="hidden"
                   name="${_csrf.parameterName}"
                   value="${_csrf.token}" />

            <input name="submit" type="submit" value="Accedi" />
        </div>
    </form>

</body>
</html>

index.jsp

<%@ taglib prefix="security"
           uri="http://www.springframework.org/security/tags" %>

<html>
<body>

    <h1>Homepage protetta</h1>

    <h2>
        Benvenuto:
        <security:authentication property="principal.username" />
    </h2>

    <form action="${pageContext.request.contextPath}/logout" method="POST">
        <input type="hidden"
               name="${_csrf.parameterName}"
               value="${_csrf.token}" />

        <input type="submit" value="Logout" />
    </form>

</body>
</html>

Nota legacy: nei progetti JSP più vecchi puoi trovare riferimenti a j_username, j_password o j_spring_security_check. Nelle configurazioni moderne i valori comuni sono username, password e /login.

Configurazione XML legacy

Nei vecchi progetti Spring Security era comune configurare tutto tramite XML, per esempio nel file spring-security.xml.

Configurazione form-login XML

<http auto-config="true" use-expressions="true">

    <form-login
        login-page="/login.jsp"
        default-target-url="/index.jsp"
        authentication-failure-url="/login.jsp?error=true"
        login-processing-url="/login" />

    <logout
        logout-url="/logout"
        logout-success-url="/login.jsp?logout=true"
        invalidate-session="true"
        delete-cookies="JSESSIONID" />

    <intercept-url pattern="/login*" access="isAnonymous()" />
    <intercept-url pattern="/css/**" access="permitAll" />
    <intercept-url pattern="/images/**" access="permitAll" />
    <intercept-url pattern="/**" access="isAuthenticated()" />

</http>

Questa configurazione indica a Spring Security quale pagina JSP usare per il login, dove inviare il form, cosa fare in caso di errore e come gestire il logout.

Welcome file in web.xml

<welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
</welcome-file-list>

Questa parte è tipica delle web application Java tradizionali basate su WAR, JSP e descrittore web.xml.

WebSecurityConfigurerAdapter e migrazione

Molti tutorial scritti per Spring Security 4 o 5 usavano WebSecurityConfigurerAdapter.

Vecchio stile

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/login", "/css/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login")
                .permitAll();
    }
}

Nei progetti moderni è preferibile migrare verso SecurityFilterChain.

Nuovo stile

@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/login", "/css/**").permitAll()
                        .anyRequest().authenticated()
                )
                .formLogin(form -> form
                        .loginPage("/login")
                        .permitAll()
                );

        return http.build();
    }
}

La logica è molto simile, ma la configurazione è più esplicita e basata su bean.

Flusso completo del login personalizzato

Il flusso completo di una maschera di login personalizzata in Spring Security può essere riassunto così:

GET /area-riservata
   ↓
Utente non autenticato
   ↓
Redirect a /login
   ↓
Visualizzazione login.html
   ↓
POST /login con username, password e CSRF
   ↓
Spring Security autentica l’utente
   ↓
Successo → redirect alla homepage o alla pagina richiesta
Errore → redirect a /login?error=true

Il punto più importante è che l’URL /login ha due ruoli diversi:

  • GET /login mostra la pagina di login;
  • POST /login processa le credenziali tramite Spring Security.

Errori comuni

Non permettere l’accesso alla pagina di login

Se la pagina di login richiede autenticazione, l’utente non potrà mai autenticarsi correttamente. Devi permettere l’accesso pubblico a /login.

.requestMatchers("/login").permitAll()

Bloccare CSS, immagini o JavaScript

Se le risorse statiche sono protette, la pagina di login può caricarsi senza stile, senza immagini o con funzionalità mancanti.

.requestMatchers("/css/**", "/js/**", "/images/**").permitAll()

Usare un action sbagliato nel form

Il form deve inviare le credenziali all’URL configurato con loginProcessingUrl.

<form th:action="@{/login}" method="post">

Dimenticare method=”post”

Il login deve essere inviato con metodo POST, non con GET.

Dimenticare il token CSRF

Se CSRF è attivo e il token non viene inviato, Spring Security può rifiutare la richiesta.

Usare nomi errati per username e password

Se il form usa email ma Spring Security si aspetta username, l’autenticazione può fallire. In quel caso bisogna configurare usernameParameter.

Esporre dettagli eccessivi negli errori

Non dire all’utente se è sbagliato lo username o la password. Usa un messaggio generico.

Usare un link GET per il logout con CSRF attivo

Con CSRF attivo, il logout dovrebbe essere eseguito tramite form POST con token CSRF.

Disabilitare CSRF senza motivo

Molti sviluppatori disabilitano CSRF per risolvere rapidamente un errore 403. In un’applicazione web con sessione, questa scelta può ridurre la sicurezza.

Best practice per login personalizzato in Spring Security

  • Usa SecurityFilterChain nei progetti moderni.
  • Permetti l’accesso pubblico a login e risorse statiche.
  • Usa sempre POST per inviare il form di login.
  • Includi il token CSRF nel form.
  • Usa messaggi di errore generici.
  • Non disabilitare CSRF senza un motivo preciso.
  • Usa BCryptPasswordEncoder o un encoder sicuro per le password.
  • Evita password in chiaro nel codice, tranne che in esempi didattici.
  • Configura correttamente il logout.
  • Usa HTTPS in produzione.
  • Proteggi tutte le pagine riservate.
  • Valuta rate limiting o protezioni contro brute force.
  • Non mostrare informazioni sensibili nella pagina di errore.
  • Testa login corretto, login fallito, logout e accesso a risorse protette.

Checklist di debug

Problema Controllo da fare
Errore 403 al login Verifica token CSRF e metodo POST.
CSS non caricato Permetti /css/** nelle regole di sicurezza.
Login sempre fallito Controlla nomi dei parametri username e password.
Pagina login in loop Verifica che /login sia accessibile a tutti.
Logout non funziona Usa form POST con token CSRF.
Redirect sbagliato dopo login Controlla defaultSuccessUrl e request salvata.
Errore template Verifica posizione del file login.html o login.jsp.

FAQ sul login personalizzato in Spring Security

Come creare una maschera di login personalizzata in Spring Security?

Devi creare una pagina di login, configurare formLogin() con loginPage(), permettere l’accesso pubblico alla pagina e inviare il form con metodo POST all’URL configurato con loginProcessingUrl().

Qual è l’URL predefinito per il login in Spring Security?

In una configurazione standard, il form viene inviato con POST a /login.

Quali sono i nomi predefiniti dei campi username e password?

I nomi predefiniti sono username e password.

Posso usare email al posto di username?

Sì. Puoi configurare usernameParameter("email") e usare un input con name="email".

Perché ricevo errore 403 quando invio il form di login?

Spesso il problema è l’assenza del token CSRF o l’uso di un metodo HTTP non corretto. Il form dovrebbe usare POST e includere il token CSRF.

Devo disabilitare CSRF per far funzionare il login?

No, normalmente non è necessario. È meglio includere correttamente il token CSRF nel form.

Come mostrare un messaggio di errore dopo login fallito?

Puoi configurare failureUrl("/login?error=true") e mostrare un messaggio nella pagina se il parametro error è presente.

Come configurare il logout?

Puoi usare logoutUrl("/logout"), logoutSuccessUrl("/login?logout=true"), invalidateHttpSession(true) e deleteCookies("JSESSIONID").

Il logout deve essere GET o POST?

Con CSRF attivo, il logout dovrebbe essere eseguito tramite POST e includere il token CSRF.

Posso usare JSP con Spring Security moderno?

Sì, ma per nuovi progetti Spring Boot spesso Thymeleaf è più semplice da configurare. JSP resta comune in applicazioni legacy.

Che fine ha fatto WebSecurityConfigurerAdapter?

Nei progetti moderni si preferisce configurare Spring Security dichiarando un bean SecurityFilterChain.

Come permettere CSS e immagini nella pagina di login?

Devi aggiungere regole come .requestMatchers("/css/**", "/images/**", "/js/**").permitAll().

Conclusione

Creare una maschera di login personalizzata in Spring Security è una delle prime personalizzazioni che si fanno in una web application reale. La pagina generata automaticamente da Spring Security è utile per test e prototipi, ma un progetto professionale richiede quasi sempre una form coerente con il design dell’applicazione.

Il meccanismo è semplice: si crea una pagina di login, si configura formLogin(), si permette l’accesso pubblico a login e risorse statiche, si inviano le credenziali con POST a /login e si include il token CSRF.

Nei progetti moderni è consigliabile usare SecurityFilterChain, Thymeleaf e configurazione Java. Nei progetti legacy è ancora possibile trovare JSP, XML, spring-security.xml, form-login e tag library di Spring Security.

Il punto più importante è non confondere la pagina di login con la logica di autenticazione: la pagina raccoglie username e password, ma è Spring Security a gestire il processo di autenticazione, sessione, CSRF, logout e accesso alle risorse protette.

Riassunto finale: una login page personalizzata in Spring Security deve essere pubblica, inviare un form POST all’URL corretto, usare i parametri attesi, includere il token CSRF e gestire correttamente errori e logout.

Risorse ufficiali utili

Categorie: Spring