Stateless JWT authentication replaces server-side sessions with a self-contained, signed token that the client stores and presents on every request, letting Spring Security authenticate and authorize without storing any session state.
Key Points: • Login validates credentials once and issues a JWT containing the user's identity, roles, and expiration. • The token is returned to the client, typically in the response body, and stored client-side (e.g., in memory or secure storage). • Every subsequent request carries the token in the Authorization: Bearer header instead of a session cookie. • A custom filter intercepts each request, validates the token's signature and expiry, and populates the SecurityContext before the request reaches the controller. • Because there's no server-side session, the API scales horizontally without sticky sessions or shared session stores.
Example: A mobile app logs in once, stores the returned JWT, and attaches it to every API call for the rest of the session; if the token expires, the app calls a refresh endpoint instead of forcing the user to log in again.
Code Example:
// 1. Login and issue token
@PostMapping("/login")
public ResponseEntity<String> login(@RequestBody LoginRequest req) {
Authentication auth = authManager.authenticate(
new UsernamePasswordAuthenticationToken(req.username(), req.password()));
String token = jwtService.generateToken(auth);
return ResponseEntity.ok(token);
}
// 2. Filter validates token on protected requests
public class JwtFilter extends OncePerRequestFilter {
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain)
throws ServletException, IOException {
String token = jwtService.extractToken(req);
if (token != null && jwtService.isValid(token)) {
SecurityContextHolder.getContext().setAuthentication(jwtService.getAuthentication(token));
}
chain.doFilter(req, res);
}
}Interview Tip: A concise interview answer is:
"I'd validate credentials at login and issue a signed JWT holding identity and roles, then return it to the client. Every subsequent request carries that token in the Authorization header, and a custom filter validates it and populates the SecurityContext, so the whole flow stays stateless and scales horizontally."