Stateless APIs with JWT
Why stateless
Form login keeps a server-side session, which does not scale well across many servers and does not suit mobile or third-party API clients. The alternative is stateless authentication with a JWT (JSON Web Token): the server issues a signed token at login, the client sends it on every request, and the server verifies the signature without storing anything.
What a JWT contains
A JWT has three dot-separated parts – header, payload (claims like the username and expiry) and a signature. Because it is signed with a secret only the server knows, the server can trust its contents without a database lookup. It is encoded, not encrypted, so never put secrets in the payload.
Issue a token at login
String jwt = Jwts.builder()
.setSubject(user.getUsername())
.claim("roles", user.getRoles())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 3600_000))
.signWith(secretKey)
.compact();
Validate it on every request
Add a filter that reads the Authorization: Bearer <token> header, verifies the signature, and populates the security context so the rest of Spring Security treats the request as authenticated:
String header = request.getHeader("Authorization");
if (header != null && header.startsWith("Bearer ")) {
String token = header.substring(7);
Claims claims = Jwts.parserBuilder().setSigningKey(secretKey)
.build().parseClaimsJws(token).getBody();
var auth = new UsernamePasswordAuthenticationToken(
claims.getSubject(), null, authoritiesFrom(claims));
SecurityContextHolder.getContext().setAuthentication(auth);
}
Configure the filter chain as SessionCreationPolicy.STATELESS and register this filter before the username/password filter.
Key points
- JWTs enable stateless auth – no server session, ideal for APIs and mobile clients.
- A JWT is signed (not encrypted) – trustworthy but never a place for secrets.
- Issue a signed token at login with claims and an expiry.
- A filter validates the
Bearertoken per request and sets the security context.