By WebNest Studio Engineering Team
Spring Boot Tutorial
JWT Authentication Concepts in Spring Boot
Traditional session-based authentication stores login state on the server, which doesn't scale cleanly across multiple server instances without shared session storage. JSON Web Tokens (JWT) solve this by putting the identity and claims directly inside a signed token that the client stores and sends with every request — the server verifies the signature and needs to keep no session state at all.
Structure of a JWT
A JWT is a string made of three Base64URL-encoded parts separated by dots: header.payload.signature. The header specifies the signing algorithm (e.g. HS256). The payload holds claims — data such as the subject (username), issued-at time, expiration time, and any custom claims like roles. The signature is computed over the header and payload using a secret key (or private key for asymmetric algorithms), and it's what lets the server detect if a token has been tampered with — without the signature verifying, the token is rejected outright.
The Stateless Authentication Flow
The typical flow has distinct steps: the client sends credentials to a login endpoint; the server verifies them and issues a signed JWT; the client stores the token (commonly in memory or a secure cookie) and attaches it as an Authorization: Bearer <token> header on every subsequent request; a security filter on the server extracts and validates the token on each request, and if valid, populates the SecurityContext with the authenticated user — all without querying a session store.
JWTs Are Not Encrypted by Default
A standard JWT (JWS — signed, not JWE — encrypted) is only signed, not encrypted — anyone can Base64-decode the payload and read its contents, they simply cannot modify it undetected without invalidating the signature. This means JWTs must never carry sensitive data like passwords, and expiration times must be kept short since a stolen valid token remains usable until it expires (unless a revocation/blacklist mechanism is added).
Example
A JWT authentication filter that validates a bearer token on each request
public class JwtAuthFilter extends OncePerRequestFilter {
private final JwtService jwtService;
public JwtAuthFilter(JwtService jwtService) { this.jwtService = jwtService; }
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {
String header = request.getHeader("Authorization");
if (header != null && header.startsWith("Bearer ")) {
String token = header.substring(7);
if (jwtService.isValid(token)) {
String username = jwtService.extractUsername(token);
var authToken = new UsernamePasswordAuthenticationToken(username, null,
jwtService.extractAuthorities(token));
SecurityContextHolder.getContext().setAuthentication(authToken);
}
}
chain.doFilter(request, response);
}
}
// Example decoded JWT payload:
// { "sub": "asha@webnest.com", "roles": ["USER"], "iat": 1758441600, "exp": 1758445200 }
Request with valid, unexpired token -> reaches controller as authenticated user "asha@webnest.com"
Request with expired token -> 401 Unauthorized, SecurityContext left unauthenticated
Request with tampered payload -> signature check fails, token rejected before claims are trusted
Common Mistakes
- Storing sensitive data (passwords, credit card numbers) inside JWT claims, forgetting that the payload is only Base64-encoded, not encrypted.
- Setting an extremely long or infinite expiration time, which makes a stolen token dangerous for a long window with no way to revoke it centrally.
- Skipping signature verification and trusting the decoded payload directly, which allows a forged token with fabricated claims to pass as valid.
- Storing the JWT in localStorage on the frontend without considering XSS risk, when an HttpOnly cookie may be a safer storage choice for browser clients.
Exercise
A teammate is about to make this mistake: Storing sensitive data (passwords, credit card numbers) inside JWT claims, forgetting that the payload is only Base64-encoded, not encrypted. Use Structure of a JWT to review the decision. Write a corrected implementation or design sketch that follows A JWT has three parts — header, payload, signature — and the signature lets the server detect tampering., then name one edge case you would check.
Show hints
- Start with Structure of a JWT and compare your approach with A JWT authentication filter that validates a bearer token on each request. Identify the decision or behavior the lesson recommends.
- Walk through your scenario step by step. Confirm that it follows A JWT has three parts — header, payload, signature — and the signature lets the server detect tampering. and that it would catch the failure described by Storing sensitive data (passwords, credit card numbers) inside JWT claims, forgetting that the payload is only Base64-encoded, not encrypted..
Quick quiz
1. Which choice best describes the recommended approach in JWT Authentication Concepts in Spring Boot?
- A. Storing sensitive data (passwords, credit card numbers) inside JWT claims, forgetting that the payload is only Base64-encoded, not encrypted.
- B. Setting an extremely long or infinite expiration time, which makes a stolen token dangerous for a long window with no way to revoke it centrally.
- C. A JWT has three parts — header, payload, signature — and the signature lets the server detect tampering.
Show answer
C. This is the recommended approach: A JWT has three parts — header, payload, signature — and the signature lets the server detect tampering. Avoid: Storing sensitive data (passwords, credit card numbers) inside JWT claims, forgetting that the payload is only Base64-encoded, not encrypted.
Interview questions
Why does this principle matter in JWT Authentication Concepts in Spring Boot: A JWT has three parts — header, payload, signature — and the signature lets the server detect tampering.
Show answer
A JWT has three parts — header, payload, signature — and the signature lets the server detect tampering. Apply this in Structure of a JWT; the example A JWT authentication filter that validates a bearer token on each request shows one way to do it. Avoid Storing sensitive data (passwords, credit card numbers) inside JWT claims, forgetting that the payload is only Base64-encoded, not encrypted. and explain how your implementation or design prevents that failure.
Key Points to Remember
- A JWT has three parts — header, payload, signature — and the signature lets the server detect tampering.
- Stateless JWT auth: login issues a token, the client sends it as a Bearer header, and a filter validates it on every request without server-side session storage.
- A standard JWT is signed but not encrypted — its payload is readable by anyone, so it must never contain sensitive data.
- Short expiration times limit the damage window if a token is stolen, since revoking an already-issued JWT is not built in by default.
Practice in the playground
Write and run your own code for this topic, or start from the exercise or an example. Runs plain Java 17. Frameworks such as Spring, and databases, are not available here. Your code for each topic is kept in this browser.
Run the examples
Change an input, predict the result, then compare it with the output. Explain why the result changes.
These examples need a JDK project with their libraries on your computer, so they can be copied but not run here. Codelab runs Python, FastAPI backends with SQLite, plain Java 17 and HTML/CSS/JavaScript.
Written by
WebNest Studio Engineering Team
Practical lessons from developers working with Java, Python, React, databases and production systems.
Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering