Vittorio Turiaci

Autenticazione JWT in Spring Boot: come strutturarla senza sbagliare

Come implementare autenticazione stateless con JWT in Spring Boot: filtro di sicurezza, refresh token e gli errori più comuni da evitare.

Spring BootJavaSicurezza

JWT risolve un problema preciso: autenticare richieste senza mantenere stato lato server. Non è "più moderno" delle sessioni, è semplicemente adatto a scenari diversi — API consumate da client diversi, architetture a microservizi, backend stateless dietro un load balancer.

Il flusso minimo

Uno schema base che uso spesso in Spring Boot con Spring Security:

  1. L'utente fa login con credenziali, il server valida e genera un access token JWT firmato (di solito con scadenza breve, 15-30 minuti).
  2. Il client invia il token nell'header Authorization: Bearer <token> su ogni richiesta successiva.
  3. Un filtro personalizzato valida la firma e la scadenza del token prima che la richiesta arrivi al controller.
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final JwtService jwtService;

    @Override
    protected void doFilterInternal(
            HttpServletRequest request,
            HttpServletResponse response,
            FilterChain filterChain
    ) throws ServletException, IOException {
        String header = request.getHeader("Authorization");

        if (header == null || !header.startsWith("Bearer ")) {
            filterChain.doFilter(request, response);
            return;
        }

        String token = header.substring(7);

        if (jwtService.isValid(token)) {
            var authentication = jwtService.getAuthentication(token);
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }

        filterChain.doFilter(request, response);
    }
}

Il filtro va registrato nella SecurityFilterChain prima del filtro standard di autenticazione via username/password, e la configurazione va impostata come stateless (SessionCreationPolicy.STATELESS): niente HttpSession, ogni richiesta si autentica da sola.

Dove mettere il segreto (e dove no)

Il secret usato per firmare i token non va mai nel codice sorgente, né in application.yml committato. Va iniettato da variabile d'ambiente o da un secret manager, e deve essere abbastanza lungo da resistere a un attacco a forza bruta (per HMAC-SHA256, almeno 256 bit).

Access token vs refresh token

Un access token con scadenza breve limita il danno se viene intercettato, ma costringe l'utente a fare login continuamente — a meno di affiancargli un refresh token con scadenza più lunga, memorizzato in modo più protetto (idealmente un cookie httpOnly, non localStorage) e usato solo per ottenere un nuovo access token tramite un endpoint dedicato.

Errori comuni

Salvare il JWT in localStorage. È accessibile da qualsiasi script, quindi vulnerabile a XSS. Un cookie httpOnly e Secure è più sicuro, anche se richiede attenzione in più per il CSRF.

Non gestire la scadenza in modo esplicito. Se il filtro ignora un token scaduto invece di rispondere 401, l'errore che arriva al client è spesso un generico 403 o un'eccezione non gestita, difficile da interpretare lato frontend.

Mettere troppi dati nel payload. Il JWT è leggibile da chiunque abbia il token (è firmato, non cifrato): ruoli e permessi vanno bene, dati sensibili no.

Conclusione

JWT non è complicato da implementare, ma è facile implementarlo in modo insicuro senza accorgersene. La parte che richiede più attenzione non è la libreria da usare, ma dove e come conservi i token lato client.