JWT Authentication Flow
In modern apps, the JWT is your wristband. Show it once and you're recognized everywhere.
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.
- 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). - jjwt 0.12.x Library: The modern Java library to create and parse JWTs.
- OncePerRequestFilter: A Spring Security filter that runs on every single HTTP request to intercept and check for the wristband (JWT).
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);
}
}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 aUsernamePasswordAuthenticationTokenthat Spring stores inSecurityContextHolder. This token tells Spring “this user is authenticated” for the rest of the request lifecycle.
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
Want to track your progress?
Log in to save your place and pick up where you left off.
Progress track karna chahte ho?
Login karo apni progress save karne ke liye aur jahan chhoda tha wahan se shuru karo.
Login