ReviseAlgo Logo

JWT & OAuth2

Implementing JWT Authentication in Spring Boot — End to End

Writing custom JWT filter, authentication tokens, and token utility classes.

Last Updated: June 14, 2026 15 min read

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:

  1. It reads the HTTP request header: Authorization.
  2. If the header is present and starts with Bearer , it extracts the token.
  3. It parses and verifies the signature of the token.
  4. If valid, it extracts the username and roles.
  5. It builds a UsernamePasswordAuthenticationToken and saves it in the thread-local SecurityContextHolder.

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?

View Solution

Exceptions thrown inside Servlet Filters (like ExpiredJwtException) occur before requests reach @ControllerAdvice error handlers. To output correct 401 statuses:

  1. Configure a custom AuthenticationEntryPoint bean in your security config that catches auth failures and writes standard RFC 7807 JSON errors directly to the HTTP response stream.
  2. Alternatively, wrap the execution inside doFilterInternal inside a try-catch block and delegate the error to the HandlerExceptionResolver to 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.