Implementing JWT Authentication in Spring Boot Applications

JSON Web Tokens consist of three distinct segments: a header defining cryptographic parameters, a payload carrying assertions, and a signature ensuring integrity. Implementing token validation in a Spring ecosystem requires orchestrating token generation, externalized configuration, request interception, and MVC registration. The following guide demonstrates a streamlined approach to establishing this security layer.

Token Generation and Parsing

A dedicated utility class encapsulates the cryptographic operations. By leveraging the JJWT library, we can construct tokens with a configurable expiration window and verify incoming credentials without tightly coupling business logic to security routines.


import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Map;

public class JwtEngine {

    /**
     * Constructs a signed token with a defined validity window.
     */
    public static String generate(Map<String, Object> payload, String key, long validityMs) {
        Date expiry = new Date(System.currentTimeMillis() + validityMs);
        return Jwts.builder()
                .setClaims(payload)
                .setExpiration(expiry)
                .signWith(SignatureAlgorithm.HS256, key.getBytes(StandardCharsets.UTF_8))
                .compact();
    }

    /**
     * Validates structure and signature, returning the extracted claims.
     */
    public static Claims verify(String key, String rawToken) {
        return Jwts.parser()
                .setSigningKey(key.getBytes(StandardCharsets.UTF_8))
                .parseClaimsJws(rawToken)
                .getBody();
    }
}

Externalized Configuration

Hardcoding secrets reduces flexibility and complicates deployment across environments. Binding properties to a configuration class allows seamless adjustments via YAML or environment variables.


import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "app.security.jwt")
@Data
public class AuthConfig {
    private String signingKey;
    private long validityMillis;
    private String headerKey;
}

The corresponding application.yaml snippet:


app:
  security:
    jwt:
      signing-key: enterprise-grade-secret-string
      validity-millis: 7200000
      header-key: Authorization

Request Interception

An interceptor acts as a gatekeeper for protected endpoints. It inspects incoming HTTP requests, validates the attached credential, and populates a request attribute for downstream controllers.


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class TokenGuard implements HandlerInterceptor {

    private static final Logger log = LoggerFactory.getLogger(TokenGuard.class);
    private final AuthConfig config;

    public TokenGuard(AuthConfig config) {
        this.config = config;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }

        String bearer = request.getHeader(config.getHeaderKey());
        if (bearer == null || bearer.isBlank()) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Missing credential");
            return false;
        }

        try {
            Claims data = JwtEngine.verify(config.getSigningKey(), bearer);
            Long targetId = Long.parseLong(data.get("sub").toString());
            request.setAttribute("authenticatedUserId", targetId);
            log.debug("Validated request for user ID: {}", targetId);
            return true;
        } catch (Exception e) {
            log.warn("Authentication rejected: {}", e.getMessage());
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid token");
            return false;
        }
    }
}

Web MVC Integration

Registering the interceptor within the Spring configuration completes the pipeline. Path patterns define the scope of enforcement, while exclusion rules prevent blocking static assets or public authentication endpoints.


import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class ApiRouting implements WebMvcConfigurer {

    private final TokenGuard tokenGuard;

    public ApiRouting(TokenGuard tokenGuard) {
        this.tokenGuard = tokenGuard;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(tokenGuard)
                .addPathPatterns("/api/v1/admin/**", "/api/v1/internal/**")
                .excludePathPatterns("/api/v1/auth/login", "/api/v1/auth/register", "/error", "/favicon.ico");
    }
}

The configuration ensures that only requests bearing valid crypttographic assertions reach the protected busniess logic, while unauthorized or malformed attempts are immediately rejected with a 401 status before controller execution begins.

Tags: spring-boot JWT web-security interceptor java

Posted on Wed, 10 Jun 2026 18:12:59 +0000 by henryblake1979