JWT-Based Authentication (Stateless Auth) in Spring Boot


Table of Contents

  1. Introduction to JWT
  2. How JWT Works (Stateless Auth)
  3. Benefits of JWT in Spring Applications
  4. Generating JWT Tokens
  5. Validating JWT Tokens
  6. Creating a Custom Filter
  7. Configuring Spring Security for JWT
  8. Testing the Full Flow
  9. Best Practices
  10. Summary

1. Introduction to JWT

JSON Web Tokens (JWT) are a compact, URL-safe means of representing claims to be transferred between two parties. In the context of Spring Security, JWTs are used to authorize users in a stateless manner—no server-side session storage is required.

A JWT typically contains three parts:

  • Header: Type and signing algorithm.
  • Payload: User information and claims.
  • Signature: Verifies the token wasn’t tampered with.

2. How JWT Works (Stateless Auth)

In JWT-based stateless authentication:

  1. The user logs in with credentials.
  2. Server authenticates and returns a signed JWT token.
  3. The client stores the JWT (usually in localStorage or cookies).
  4. For each request, the client sends the token in the Authorization header.
  5. The server verifies the token and processes the request.

3. Benefits of JWT in Spring Applications

  • Stateless: No session storage on server.
  • Scalable: Ideal for distributed systems.
  • Self-contained: Carries user data and metadata.
  • Cross-platform: Can be used across languages and services.

4. Generating JWT Tokens

Use a utility class to generate tokens:

javaCopyEditpublic class JwtUtil {
    private final String SECRET_KEY = "your-secret";

    public String generateToken(UserDetails userDetails) {
        return Jwts.builder()
            .setSubject(userDetails.getUsername())
            .claim("roles", userDetails.getAuthorities())
            .setIssuedAt(new Date(System.currentTimeMillis()))
            .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // 10 hrs
            .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
            .compact();
    }
}

5. Validating JWT Tokens

javaCopyEditpublic boolean validateToken(String token, UserDetails userDetails) {
    final String username = extractUsername(token);
    return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}

You’ll also need methods like extractUsername(), extractExpiration() etc., using Jwts.parser().


6. Creating a Custom JWT Filter

This filter intercepts requests, extracts the JWT, and sets the security context.

javaCopyEditpublic class JwtRequestFilter extends OncePerRequestFilter {

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
        throws ServletException, IOException {

        final String authHeader = request.getHeader("Authorization");

        String username = null;
        String jwt = null;

        if (authHeader != null && authHeader.startsWith("Bearer ")) {
            jwt = authHeader.substring(7);
            username = jwtUtil.extractUsername(jwt);
        }

        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);

            if (jwtUtil.validateToken(jwt, userDetails)) {
                UsernamePasswordAuthenticationToken token =
                    new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());

                token.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

                SecurityContextHolder.getContext().setAuthentication(token);
            }
        }

        chain.doFilter(request, response);
    }
}

7. Configuring Spring Security for JWT

Update your security configuration:

javaCopyEdit@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Autowired
    private JwtRequestFilter jwtRequestFilter;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeHttpRequests()
            .requestMatchers("/authenticate").permitAll()
            .anyRequest().authenticated()
            .and()
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }
}

8. Testing the Full Flow

  1. Authenticate:
    • POST to /authenticate with username and password.
    • Receive JWT token.
  2. Access Protected Resource:
    • Set header: Authorization: Bearer <token>
    • GET /api/protected – success if token is valid.

9. Best Practices

  • Always sign tokens with a strong secret or RSA keys.
  • Use HTTPS to secure tokens in transit.
  • Set short expiration times and refresh tokens.
  • Avoid storing JWTs in localStorage on browsers prone to XSS.
  • Use claim fields wisely—don’t overstuff JWTs.

10. Summary

JWT-based authentication provides a scalable, stateless solution for securing your Spring Boot APIs. It avoids session storage, making it ideal for RESTful microservices.

Key Concepts Covered:

  • What JWT is and how it works
  • How to generate, sign, and validate tokens
  • Creating a custom JWT filter
  • Securing endpoints using JWT