Chapter 7.3☕ 18 min read

JWT Authentication Flow

In modern apps, the JWT is your wristband. Show it once and you're recognized everywhere.

01The Concept: Stateless Tokens

The Goa Resort Wristband Analogy:

When you check into a beach resort in Goa, you pay at the front desk (Login). The receptionist gives you a waterproof wristband (JWT).

For the rest of your stay, you don’t go back to the front desk. If you want a drink at the pool bar, you just show your wristband. The bartender verifies the wristband’s stamp and serves you. The bartender doesn’t need to call the front desk.

The front desk is your /login API. The wristband is the JWT. The bartender is the Spring Security Filter. The JWT contains your identity, and Spring verifies it on every request without checking the database.

02Technical Explanation
  1. JWT Structure: A JWT is a string xxxxx.yyyyy.zzzzz. It has 3 parts: Header, Payload (data like username), and Signature (to prove it wasn’t tampered with).
  2. jjwt 0.12.x Library: The modern Java library to create and parse JWTs.
  3. OncePerRequestFilter: A Spring Security filter that runs on every single HTTP request to intercept and check for the wristband (JWT).
03Full Working Code: JWT Service and Filter

First, add the modern jjwt dependencies to pom.xml.

<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.12.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.12.5</version>
<scope>runtime</scope>
</dependency>

1. The JWT Utility Service (JwtService.java)

package com.devinhyderabad;

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import javax.crypto.SecretKey;
import java.util.Date;

public class JwtService {

private final String SECRET = "my-super-secret-key-must-be-at-least-32-bytes-long!!";
private final SecretKey key = Keys.hmacShaKeyFor(SECRET.getBytes());

// 1. Generate Token (Issue the wristband)
public String generateToken(String username) {
return Jwts.builder()
.subject(username)
.issuedAt(new Date())
.expiration(new Date(System.currentTimeMillis() + 86400000)) // 24 hours
.signWith(key)
.compact();
}

// 2. Validate Token (Check the wristband stamp)
public String extractUsername(String token) {
return Jwts.parser()
.verifyWith(key)
.build()
.parseSignedClaims(token)
.getPayload()
.getSubject();
}
}

2. The Security Filter (JwtAuthenticationFilter.java)

package com.devinhyderabad;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.Collections;

public class JwtAuthenticationFilter extends OncePerRequestFilter {

private final JwtService jwtService;

public JwtAuthenticationFilter(JwtService jwtService) {
this.jwtService = jwtService;
}

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {

// 1. Get Authorization header
String authHeader = request.getHeader("Authorization");

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

// 2. Extract token (remove "Bearer ")
String jwt = authHeader.substring(7);

// 3. Validate and get username
String username = jwtService.extractUsername(jwt);

if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
// 4. Set authentication in Spring Security context
UsernamePasswordAuthenticationToken authToken =
new UsernamePasswordAuthenticationToken(username, null, Collections.emptyList());
SecurityContextHolder.getContext().setAuthentication(authToken);
}

filterChain.doFilter(request, response);
}
}
04Code Walkthrough

JwtService handles two operations:

  • generateToken: Creates a JWT with subject (username), issue/expiry dates, and signs it using HMAC-SHA key. The resulting token is a base64url-encoded three-part string.
  • extractUsername: Parses a token back, verifies its signature using the same secret key, and extracts the subject (username) from the payload.

JwtAuthenticationFilter extends OncePerRequestFilter:

  • It intercepts every HTTP request and looks for an Authorization: Bearer <token> header.
  • If found, it extracts the JWT, validates it via JwtService, and creates a UsernamePasswordAuthenticationToken that Spring stores in SecurityContextHolder. This token tells Spring “this user is authenticated” for the rest of the request lifecycle.
05Why It Matters / Interview Note

Interview Question: “How do you configure the JwtAuthenticationFilter in Spring Security 6?”

Answer: We inject it into the SecurityFilterChain bean using http.addFilterBefore(new JwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class). We also set the session management to SessionCreationPolicy.STATELESS so Spring doesn’t create HTTP sessions.

Enterprise Note: Never put sensitive data (like passwords or SSNs) inside a JWT payload. A JWT is only signed (to prevent tampering), not encrypted. Anyone who intercepts the token can decode the payload part (the yyyyy section) and read its contents in plain text using base64.

Key Takeaways

  • ✅ JWT is a stateless token (xxxxx.yyyyy.zzzzz): Header, Payload, Signature
  • ✅ jjwt 0.12.x is the modern library for JWT creation and parsing
  • ✅ OncePerRequestFilter intercepts every HTTP request to validate JWT
  • ✅ JWT is signed (not encrypted) — do NOT store sensitive data in payload
  • ✅ SessionCreationPolicy.STATELESS prevents Spring from creating HTTP sessions