LogIn
I don't have account.

Step-by-Step JWT Implementation in Java (Spring Boot)

Code Crafter

120 Views

JWT (JSON Web Token) is a compact, URL-safe means of representing claims securely between two parties. In the Java ecosystem, JWTs are widely used for authentication and authorization in RESTful services.

In this article, you will learn a step-by-step to implement JWT (JSON Web Token) in Java, typically using Spring Boot, JJWT (Java JWT library), and Spring Security.

Prerequisites

  • Java 8+
  • Spring Boot (2.x or 3.x)
  • Maven or Gradle
  • Dependency: jjwt for JWT handling

1. Add Dependencies

Maven
Copy
<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt-api</artifactId>
  <version>0.11.5</version>
</dependency>
<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt-impl</artifactId>
  <version>0.11.5</version>
  <scope>runtime</scope>
</dependency>
<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt-jackson</artifactId> 
  <version>0.11.5</version>
  <scope>runtime</scope>
</dependency>

2. Create a JWT Utility Class

Create a JWT Utility Class to create and validate JWT token.

Copy
package com.example.demo.security;

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.springframework.stereotype.Component;

import java.security.Key;
import java.util.Date;

@Component
public class JwtUtil {

    private final Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
    private final long expirationTime = 1000 * 60 * 60; // 1 hour

    public String generateToken(String username, Long userId, String role) {
        return Jwts.builder()
                .setSubject(username)
                .claim("userId", userId)
                .claim("role", role)
                .setIssuer("demoApp")
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + expirationTime))
                .signWith(key)
                .compact();
    }

    public boolean validateToken(String token) {
        try {
            Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token);
            return true;
        } catch (JwtException e) {
            return false;
        }
    }

    public Claims extractAllClaims(String token) {
        return Jwts.parserBuilder()
                .setSigningKey(key)
                .build()
                .parseClaimsJws(token)
                .getBody();
    }

    public String extractUsername(String token) {
        return extractAllClaims(token).getSubject();
    }

    public Long extractUserId(String token) {
        return extractAllClaims(token).get("userId", Long.class);
    }

    public String extractRole(String token) {
        return extractAllClaims(token).get("role", String.class);
    }
}

3. Create Authentication Endpoint

Copy
package com.example.demo.controller;

import com.example.demo.model.AuthRequest;
import com.example.demo.model.AuthResponse;
import com.example.demo.security.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;

@RestController
public class AuthController {

    @Autowired
    private JwtUtil jwtUtil;

    @PostMapping("/authenticate")
    public ResponseEntity<?> authenticate(@RequestBody AuthRequest request) {
        // Validate username/password (in production, use a proper user service)
        if ("admin".equals(request.getUsername()) &amp;&amp; "password".equals(request.getPassword())) {
            String token = jwtUtil.generateToken("admin", 101L, "USER");
            return ResponseEntity.ok(new AuthResponse(token));
        }
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid credentials");
    }
}

AuthRequest and AuthResponse Classes

Copy
public class AuthRequest {
    private String username;
    private String password;
    // Getters and Setters
}

public class AuthResponse {
    private String token;
    public AuthResponse(String token) { this.token = token; }
    public String getToken() { return token; }
}

4. Return Bookmarked Articles API (JWT Protected)

Copy
@RestController
@RequestMapping("/api")
public class BookmarkController {

    @Autowired
    private JwtUtil jwtUtil;

    @GetMapping("/bookmarks")
    public ResponseEntity<?> getBookmarks(@RequestHeader("Authorization") String authHeader) {
        if (authHeader == null || !authHeader.startsWith("Bearer ")) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Missing or invalid token");
        }

        String token = authHeader.substring(7);

        if (!jwtUtil.validateToken(token)) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Token is invalid or expired");
        }

        Long userId = jwtUtil.extractUserId(token);
        String username = jwtUtil.extractUsername(token);

        // Mocked list of articles based on user
        List<String> bookmarks = List.of(
                "How JWT Works – A Guide for " + username,
                "Spring Boot Security Tips",
                "Top 10 Java Interview Questions"
        );

        return ResponseEntity.ok(Map.of(
                "user", username,
                "userId", userId,
                "bookmarks", bookmarks
        ));
    }
}

In above api we are validating token this is not scalable because more endpoints = more repeated logic and higher chance of errors. To resolve this we need to do this on a centralize point. that can be achieved by creating JwtFilter and SecurityConfig files

Create JwtFilter to Intercept Requests

The JwtFilter acts as a centralized gatekeeper that runs before your controller logic. It extracts the JWT from the request, validates it, and sets the authenticated user in Spring Security’s SecurityContext. By the time your controller handles the request, the user is already authenticated and accessible via SecurityContextHolder.

Imagine a security guard at the gate (JwtFilter) That If all is good, the person is allowed into the building (controller) without needing to show ID again.

Copy
package com.example.demo.security;

import jakarta.servlet.*;
import jakarta.servlet.http.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.Collections;

@Component
public class JwtFilter extends OncePerRequestFilter {

    @Autowired
    private JwtUtil jwtUtil;

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

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

        if (authHeader != null &amp;&amp; authHeader.startsWith("Bearer ")) {
            String token = authHeader.substring(7);

            if (jwtUtil.validateToken(token)) {
                String username = jwtUtil.extractUsername(token);

                UsernamePasswordAuthenticationToken authToken =
                        new UsernamePasswordAuthenticationToken(username, null, Collections.emptyList());

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

        filterChain.doFilter(request, response);
    }
}

Configure Spring Security (SecurityConfig.java)

SecurityConfig defines the security rules for your application. It specifies which routes are public (like /authenticate) and which ones require authentication (like /api/**). It also integrates your custom JwtFilter into Spring’s security pipeline.

Copy
package com.example.demo.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.*;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.*;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Autowired
    private JwtFilter jwtFilter;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
                .csrf().disable()
                .authorizeHttpRequests()
                .requestMatchers("/authenticate").permitAll()
                .anyRequest().authenticated()
                .and()
                .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
                .build();
    }
}