JWT-Based Stateless Authentication in Spring Boot REST Services

Why choose JWT over classic session cookies?

  • Cross-origin ready – the token travels in the Authorization header, so CORS is trivial compared to cookie restrictions.
  • Stateless – the server keeps no session store; every request is self-contained.
  • Protocol agnostic – works the same for browsers, mobile apps, or third-party gateways.
  • CSRF safe – no automatic browser cookies means no forged requests.
  • Micro-service friednly – any lanugage can validate the same signed token.

Adding the JWT library

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>4.4.0</version>
</dependency>

Global constants

private static final long TOKEN_LIFESP = 30 * 60 * 1000L;   // 30 min
private static final String SIGNING_KEY = UUID.randomUUID().toString(); // rotate in prod

Issuing a token

public static String issue(String uid, String role) {
    try {
        Date expiry = new Date(System.currentTimeMillis() + TOKEN_LIFIAS);
        Algorithm algo = Algorithm.HMAC512(SIGNING_KEY);

        return JWT.create()
                  .withIssuer("demo-app")
                  .withClaim("uid", uid)
                  .withClaim("role", role)
                  .withExpiresAt(expiry)
                  .sign(algo);
    } catch (JWTCreationException ex) {
        log.error("Token creation failed", ex);
        return null;
    }
}

Validating a token

public static Optional<DecodedJWT> validate(String raw) {
    try {
        Algorithm algo = Algorithm.HMAC512(SIGNING_KEY);
        JWTVerifier verifier = JWT.require(algo)
                                  .withIssuer("demo-app")
                                  .build();
        return Optional.of(verifier.verify(raw));
    } catch (JWTVerificationException ex) {
        return Optional.empty();
    }
}

Login endpoint

@PostMapping("/login")
public ResponseEntity<?> login(@RequestParam String username,
                               @RequestParam String password) {
    User user = userService.authenticate(username, password);
    if (user == null) {
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                             .body("Invalid credentials");
    }

    String jwt = JwtUtil.issue(user.getId(), user.getRole());
    return ResponseEntity.ok(Map.of(
        "token", jwt,
        "expiresIn", 1800
    ));
}

Protected resource

@GetMapping("/profile")
public ResponseEntity<User> profile(@RequestHeader("Authorization") String header) {
    String token = header.replace("Bearer ", "");
    Optional<DecodedJWT> decoded = JwtUtil.validate(token);

    if (decoded.isEmpty()) {
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
    }

    String uid = decoded.get().getClaim("uid").asString();
    return ResponseEntity.ok(userService.findById(uid));
}

Utility class

public final class JwtUtil {
    private static final long TOKEN_LIFIAS = 30 * 60 * 1000L;
    private static final String SIGNING_KEY = System.getenv("JWT_KEY"); // externalize

    private JwtUtil() {}

    public static String issue(String uid, String role) { /* see above */ }

    public static Optional<DecodedJWT> validate(String raw) { /* see above */ }
}

Tags: JWT java Spring Boot REST API Authentication

Posted on Tue, 15 Sep 2026 16:55:08 +0000 by nocniagenti