Component Inspector
Click on any component in the architecture to understand how stateless JWT authentication works.
Simulation Legend
Stateless Authentication with JWT
JSON Web Tokens (JWT) provide a stateless mechanism for authenticating API requests. Instead of storing a session ID in server memory/database, the server cryptographically signs a JSON payload containing the user's identity and roles, and sends it to the client.
1. Header
Specifies the algorithm used to sign the token (e.g., HS256) and the token type (JWT).
2. Payload (Claims)
Contains the actual data: `sub` (username), `exp` (expiration time), and custom claims like roles.
3. Signature
Created by hashing the Header + Payload using a secret key known *only* to the server. Ensures integrity.
Core Implementations
1. The JwtService (Using io.jsonwebtoken library)
This class is responsible for generating new tokens during login and validating incoming tokens on subsequent requests.
@Service
public class JwtService {
@Value("${jwt.secret}")
private String secretKey; // Must be 256-bit+ secure key
public String generateToken(UserDetails userDetails) {
return Jwts.builder()
.setSubject(userDetails.getUsername())
.claim("roles", userDetails.getAuthorities())
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // 10 hours
.signWith(getSigningKey(), SignatureAlgorithm.HS256)
.compact();
}
public String extractUsername(String token) {
return Jwts.parserBuilder()
.setSigningKey(getSigningKey())
.build()
.parseClaimsJws(token) // Throws exception if signature is invalid or expired
.getBody()
.getSubject();
}
}
2. The JwtAuthenticationFilter
This filter intercepts every incoming request. It extracts the token from the `Authorization: Bearer` header, validates it using the `JwtService`, and populates the Spring `SecurityContextHolder` so controllers know who is making the request.
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired private JwtService jwtService;
@Autowired private UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String authHeader = request.getHeader("Authorization");
// 1. Check if token exists
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
chain.doFilter(request, response);
return;
}
// 2. Extract Token
final String jwt = authHeader.substring(7);
final String username = jwtService.extractUsername(jwt);
// 3. If token valid and context is empty, set authentication
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
if (jwtService.isTokenValid(jwt, userDetails)) {
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
// Save user details to Context for this request lifecycle
SecurityContextHolder.getContext().setAuthentication(authToken);
}
}
// 4. Continue filter chain
chain.doFilter(request, response);
}
}