← All articles
Backend1 min read

Securing Spring Boot APIs with JWT

A practical walkthrough of stateless authentication in Spring Security using JSON Web Tokens and a custom filter.

S

Swapnika Voora

Author

Stateless APIs don't keep server-side sessions. Instead, each request carries a signed JWT that proves who the caller is. Spring Security makes this pattern straightforward once you understand the filter chain.

The security configuration

In modern Spring Security you define a SecurityFilterChain bean rather than extending a base class.

SecurityConfig.java
@Configuration
@EnableWebSecurity
public class SecurityConfig {
 
    private final JwtAuthFilter jwtAuthFilter;
 
    public SecurityConfig(JwtAuthFilter jwtAuthFilter) {
        this.jwtAuthFilter = jwtAuthFilter;
    }
 
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
            .csrf(csrf -> csrf.disable())
            .sessionManagement(s -> s.sessionCreationPolicy(STATELESS))
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/auth/**").permitAll()
                .anyRequest().authenticated())
            .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
            .build();
    }
}

Validating the token

A custom filter runs once per request. It extracts the bearer token, verifies the signature, and populates the security context.

JwtAuthFilter.java
@Component
public class JwtAuthFilter extends OncePerRequestFilter {
 
    private final JwtService jwt;
 
    @Override
    protected void doFilterInternal(HttpServletRequest req,
                                    HttpServletResponse res,
                                    FilterChain chain) throws ServletException, IOException {
        String header = req.getHeader("Authorization");
        if (header != null && header.startsWith("Bearer ")) {
            String token = header.substring(7);
            if (jwt.isValid(token)) {
                var auth = jwt.toAuthentication(token);
                SecurityContextHolder.getContext().setAuthentication(auth);
            }
        }
        chain.doFilter(req, res);
    }
}

Signing keys matter

Use a strong secret (256 bits or more for HS256) and keep it out of source control. Rotate keys periodically and reject tokens whose expiry has passed.

Summary

  • Configure a stateless SecurityFilterChain.
  • Add a OncePerRequestFilter to validate JWTs.
  • Guard your signing key and always check expiration.

This gives you scalable, session-free authentication that works well behind load balancers.

#java#spring-security#jwt#auth

More in Backend