JWT & OAuth2
Implementing JWT Authentication in Spring Boot — End to End
Writing custom JWT filter, authentication tokens, and token utility classes.
Introduction
To use JWT in a Spring Boot application, we must replace the default session-based configuration with a stateless architecture. This is accomplished by disabling HTTP sessions, writing a custom utility class to parse JWTs, and configuring a security filter to extract, validate, and bind user identities to Spring's SecurityContext.
Why It Matters
Out of the box, Spring Security relies on HTTP session cookies. For stateless REST APIs, this behavior is problematic because cookies introduce CSRF vulnerabilities and do not scale across load balancers. Integrating custom JWT filters ensures every request is validated state-independently using standard Bearer Token headers (e.g. Authorization: Bearer <token>).
Real-World Analogy
Think of the custom JWT filter like a ticket checker at a museum. Instead of keeping a list of every checked-in user at the desk, the checker stands at the entrance, inspects the ticket (JWT) presented by the visitor, verifies the barcode (signature), and stamps their hand (authenticates the request) before letting them proceed to the exhibits.
Detailed Mechanics
1. Custom JWT Filter Lifecycle
The custom filter extends OncePerRequestFilter to ensure it runs exactly once per client query request:
- It reads the HTTP request header:
Authorization. - If the header is present and starts with
Bearer, it extracts the token. - It parses and verifies the signature of the token.
- If valid, it extracts the username and roles.
- It builds a
UsernamePasswordAuthenticationTokenand saves it in the thread-localSecurityContextHolder.
Practical Example
Below is a complete, production-ready implementation of a JWT utility class, custom validation filter, and security filter chain setup:
The custom interceptor filter that checks incoming headers:
The security configuration bean integrating stateless settings and registering the custom filter:
Quick Quiz
Q1: Why do we specify SessionCreationPolicy.STATELESS when configuring Spring Security for JWT APIs?
A) To speed up database connections.
B) To instruct Spring Security to never create or store SecurityContext sessions in HTTP server cookies, making the application stateless.
C) It automatically encrypts all database passwords.
D) It prevents browser-based cross-origin preflight checks.
Answer: B — Stateless policy guarantees that every request must provide its authentication details independently (via JWT), enforcing API statelessness.
Q2: Why must the custom JwtAuthenticationFilter be registered before UsernamePasswordAuthenticationFilter?
A) To override default CORS configs.
B) Spring throws a compiler error if filters are not in alphabetical order.
C) So that it intercepts and authenticates the token before Spring Security attempts to trigger default form-based username/password checks.
D) Custom filters can only be registered at the start of the chain.
Answer: C — Intercepting request authentication early ensures subsequent security context evaluations are satisfied without requesting login pages.
Scenario-Based Challenge
Production Scenario:
Your React application sends JWTs to your Spring Boot REST API. However, if a user sends an expired token, the API returns a standard 500 Internal Server Error page instead of a clean, structured JSON 401 Unauthorized response. How do you resolve this?
Exceptions thrown inside Servlet Filters (like ExpiredJwtException) occur before requests reach @ControllerAdvice error handlers.
To output correct 401 statuses:
- Configure a custom
AuthenticationEntryPointbean in your security config that catches auth failures and writes standard RFC 7807 JSON errors directly to the HTTP response stream. - Alternatively, wrap the execution inside
doFilterInternalinside a try-catch block and delegate the error to theHandlerExceptionResolverto map it to standard controllers-level handlers.
Interview Questions
1. Conceptual: Why should you avoid storing JWTs inside browser LocalStorage?
LocalStorage has no protection against Cross-Site Scripting (XSS) attacks. If an attacker injects a malicious script, they can read the token directly from local storage. Storing JWTs inside HTTP-only, Secure cookies with SameSite configuration mitigates this, preventing JavaScript from reading them.
2. Concept: How can you inject custom claims (e.g. user department) into a JWT in Spring Boot?
When generating the token using the builder library, call the claim(key, value) method on the JWT builder, or add a custom Map to the builder claims:
Jwts.builder().setClaims(claimsMap).setSubject(username)....
Production Considerations
Inject signing keys dynamically using environment variables or cloud secret managers (like AWS Secrets Manager or HashiCorp Vault) rather than hardcoding them in properties. Ensure validation rules include validating the token audience (aud) claim to verify target server clearances.