JWT Authentication using Spring Boot 3 & Spring Security

JSON Web Tokens (JWT) is simple, secure, stateless way for authentication. It is most widely used for authenticate users and creating secure API endpoints, by making them an ideal choice for token-based authentication.

JWT Authentication using spring boot 3

JWT Structure

A JWT consists of three parts: the Header, Payload, and Signature.

  • Header– The header typically consists of two parts— the type of token (JWT) and the signing algorithm being used (such as HMAC SHA256 or RSA).
  • Payload– The payload contains the claims or data you want to transmit, such as user information (like user ID, roles, etc.).
  • Signature– The signature is created by taking the encoded header, the encoded payload, a secret key, and then signing them using the algorithm specified in the header.

How JWT Authentication Works

  • When a user logs in, the server verifies the credentials. If valid, it generates a JWT and sends it back to the client.
  • The client stores this JWT, often in local storage or cookies, and attaches it to the headers of subsequent API requests.
  • The server then verifies the JWT in each request. If valid, the server allows access; otherwise, it rejects the request.

Since JWTs are stateless, the server doesn’t need to store any session data. This makes JWT a scalable solution for user authentication.

Implementation with Spring Boot 3 & Spring Security

What We’ll Build

We’ll develop an API that offers both public and protected endpoints. Some routes will be accessible to everyone without authentication, while others will require a valid JWT token to access.

API Endpoints

  • [POST] /auth/signup – Register a new user.
  • [POST] /auth/login – Authenticate an existing user and issue a JWT.
  • [GET] /getAllUsers – Retrieve a list of all authenticated users; this route is protected and requires authentication.

Authentication Requirements

  • The /auth/signup and /auth/login endpoints are open and don’t need any authentication.
  • The /getAllUsers endpoint is secured and can only be accessed by users who have successfully authenticated.

step-1: Create Spring Boot project and add the following dependencies in pom.xml


<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <scope>runtime</scope>
    </dependency>

    <!-- JWT Dependencies -->
    <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>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-jackson</artifactId>
        <version>0.11.5</version>
    </dependency>
</dependencies>
    

step-2: Configure your database details in application.properties file


# Server configuration
server.port = 8082

# Database configuration
spring.datasource.url=jdbc:mysql://localhost:3306/Demo
spring.datasource.name=Demo
spring.datasource.username=Admin
spring.datasource.password=admin
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# JPA configuration
spring.jpa.hibernate.ddl-auto=update
    

These properties configure the server port, database connection, and JPA behavior for your application.

step-3: Create the JWT service class


package com.jwt.example.service;

import java.security.Key;
import java.util.Date;
import java.util.Map;
import java.util.function.Function;

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;

@Service
public class JwtService {

    private static String secretKey = "YUhSMGNITTZMeTkzWldKaGRHVXRhRzkzZEM1amIyMGlhWE1pTkdjdE9TNWpiMjA9"; // Correct Base64 encoded key
    private long jwtExpiration = 360000; // Expiration in milliseconds (e.g., 10 minutes)

    // Extract username from the token
    public static String extractUsername(String token) {
        return extractClaim(token, Claims::getSubject);
    }

    // Method now allows any type of claim to be extracted
    public static  T extractClaim(String token, Function claimsResolver) {
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }

    // Generate token with extra claims
    public String generateToken(Map extraClaims, UserDetails userDetails) {
        return buildToken(extraClaims, userDetails, jwtExpiration);
    }

    // Build the JWT token
    private String buildToken(Map extraClaims, UserDetails userDetails, long expiration) {
        return Jwts.builder()
                .setClaims(extraClaims)
                .setSubject(userDetails.getUsername())
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + expiration))
                .signWith(getSignInKey(), SignatureAlgorithm.HS256)
                .compact();
    }

    public long getExpirationTime() {
        return jwtExpiration;
    }

    // Extract all claims from the token
    private static Claims extractAllClaims(String token) {
        return Jwts.parserBuilder()
                .setSigningKey(getSignInKey())
                .build()
                .parseClaimsJws(token)
                .getBody();
    }

    // Check if the token is valid
    public boolean isTokenValid(String token, UserDetails userDetails) {
        final String username = extractUsername(token);
        return (username.equals(userDetails.getUsername())) && !isTokenExpired(token);
    }

    // Check if the token has expired
    private boolean isTokenExpired(String token) {
        return extractExpiration(token).before(new Date());
    }

    // Extract the expiration date from the token
    private Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }

    // Decode the secret key
    private static Key getSignInKey() {
        byte[] keyBytes = Decoders.BASE64.decode(secretKey);
        return Keys.hmacShaKeyFor(keyBytes);
    }
}

1. Secret Key Handling (secretKey)

  • The secret key is Base64-encoded and is used to sign the JWT.
  • It is decoded and converted into a Key object using getSignInKey().

2. Token Generation (generateToken())

  • Creates a JWT with extra claims, subject (username), issued time, expiration time, and signs it using HMAC SHA-256 algorithm.
  • Calls buildToken() to construct the token.

3. Extracting Claims (extractClaim())

  • Extracts specific claims (such as username or expiration time) from the JWT.
  • Uses extractAllClaims() to parse the token.

4. Token Validation (isTokenValid())

  • Ensures the token is associated with the correct user.
  • Checks if the token has expired.

5. Checking Token Expiry (isTokenExpired())

  • Determines whether the token has expired based on the expiration date.

6. Extracting Username (extractUsername())

  • Retrieves the username (subject) from the JWT.

7. Expiration Time(getExpirationTime())

  • Defines how long the JWT remains valid (in milliseconds).

step-4: Create User entity class which implements UserDetails Interface


package com.jwt.example.entity;

import java.util.Collection;
import java.util.List;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity
public class User implements UserDetails {
	
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(nullable = false)
    private Integer id;
	
    private String fullName;
    private String email;
    private String password;

    public User(Integer id, String fullName, String email, String password) {
        super();
        this.id = id;
        this.fullName = fullName;
        this.email = email;
        this.password = password;
    }
    
    public User() {
    	
    }

    //Getter and Setters

    //Override abstract method of UserDetails interface
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return List.of();
    }

    @Override
    public String getUsername() {
        return email;
    }
    
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

step-5: Create User Repository interface which extends JpaRepository Interface


package com.jwt.example.repository;

import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.jwt.example.entity.User;

@Repository
public interface UserRepo extends JpaRepository<User, Integer> {
	
    Optional<User> findByEmail(String email);

}

step-6: Create JWTConfiguration Class


package com.jwt.example.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import com.jwt.example.repository.UserRepo;

@Configuration
public class JWTConfiguration {
	
    @Autowired
    private UserRepo userRepo;
	
    @Bean
    UserDetailsService userDetailsService() {
        return username -> userRepo.findByEmail(username)
            .orElseThrow(() -> new UsernameNotFoundException("User not found"));
    }
	
    @Bean
    BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
        return config.getAuthenticationManager();
    }

    @Bean
    AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(userDetailsService());
        authProvider.setPasswordEncoder(passwordEncoder());

        return authProvider;
    }

}

step-7: Create JWTAuthenticationEntiryPoint class which extends OncePerRequestFilter class


package com.jwt.example.service;

import java.io.IOException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.servlet.HandlerExceptionResolver;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;


@Component
public class JWTAuthenticationEntiryPoint extends OncePerRequestFilter {
	
    @Autowired
    private JwtService jwtService;
	
    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private HandlerExceptionResolver handlerExceptionResolver;
	
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        final String authHeader = request.getHeader("Authorization");
		 
        if (authHeader == null || !authHeader.startsWith("Bearer ")) {
            filterChain.doFilter(request, response);
            return;
        }
		 
        try {
            final String jwt = authHeader.substring(7);
            final String userEmail = JwtService.extractUsername(jwt);

            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

            if (userEmail != null && authentication == null) {
                UserDetails userDetails = this.userDetailsService.loadUserByUsername(userEmail);

                if (jwtService.isTokenValid(jwt, userDetails)) {
                    UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
                        userDetails,
                        null,
                        userDetails.getAuthorities()
                    );

                    authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    SecurityContextHolder.getContext().setAuthentication(authToken);
                }
            }

            filterChain.doFilter(request, response);
            
        } catch (Exception exception) {
            handlerExceptionResolver.resolveException(request, response, null, exception);
        }
		
    }

}

step-8: Create SecurityConfiguration class


package com.jwt.example.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import com.jwt.example.service.JWTAuthenticationEntiryPoint;

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
	
    @Autowired
    AuthenticationProvider authenticationProvider;
	
    @Autowired
    JWTAuthenticationEntiryPoint  jwtAuthenticationEntiryPoint;
	
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        // Configuring security filter chain
        return httpSecurity
            .csrf(csrf -> csrf.disable()) // Disable CSRF for stateless APIs
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/auth/**").permitAll() // Allow unauthenticated access to /auth/** routes
                .anyRequest().authenticated() // All other routes require authentication
            )
            .sessionManagement(session -> session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS) // Set session creation policy to stateless
            )
            .authenticationProvider(authenticationProvider) // Inject the authentication provider
            .addFilterBefore(jwtAuthenticationEntiryPoint, UsernamePasswordAuthenticationFilter.class) // Add JWT filter
            .build();
    }

}

step-9: Create UserService class


package com.jwt.example.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import com.jwt.example.dto.LoginUserDto;
import com.jwt.example.entity.User;
import com.jwt.example.repository.UserRepository;

@Service
public class UserService {
	
    @Autowired
    private UserRepository userRepo;
	
    @Autowired
    private PasswordEncoder passwordEncoder;
	
    @Autowired
    private AuthenticationManager authenticationManager;
	
    public User signup(User userData) {
       
        userData.setPassword(passwordEncoder.encode(userData.getPassword()));
        return userRepo.save(userData);
    }
	 
    public User authenticate(LoginUserDto loginUserDto) {
        authenticationManager.authenticate(
            new UsernamePasswordAuthenticationToken(
                loginUserDto.getEmail(), loginUserDto.getPassword()
            )
        );

        return userRepo.findByEmail(loginUserDto.getEmail())
                .orElseThrow();
    }
	 
    public List<User> getAllUser() {
        return userRepo.findAll();
    }
}

step-10: Create userController class

  
  package com.jwt.example.controller;
  
  import java.util.HashMap;
  import java.util.List;
  
  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.http.ResponseEntity;
  import org.springframework.web.bind.annotation.GetMapping;
  import org.springframework.web.bind.annotation.PostMapping;
  import org.springframework.web.bind.annotation.RequestBody;
  import org.springframework.web.bind.annotation.RestController;
  
  import com.jwt.example.dto.LoginUserDto;
  import com.jwt.example.entity.User;
  import com.jwt.example.response.LoginResponse;
  import com.jwt.example.service.JwtService;
  import com.jwt.example.service.UserService;
  
  @RestController
  public class UserController {
    
      @Autowired
      private JwtService jwtService;
    
      @Autowired 
      private UserService userService;
    
    
      @PostMapping("/auth/signup")
      public ResponseEntity<User> register(@RequestBody User user) {
          User registeredUser = userService.signup(user);
  
          return ResponseEntity.ok(registeredUser);
      }
  
      @PostMapping("/auth/login")
      public ResponseEntity<LoginResponse> authenticate(@RequestBody LoginUserDto loginUserDto) {
          User authenticatedUser = userService.authenticate(loginUserDto);
  
          String jwtToken = jwtService.generateToken(new HashMap<>(), authenticatedUser);
  
          LoginResponse loginResponse = new LoginResponse();
          loginResponse.setToken(jwtToken);
          loginResponse.setExpiresIn(jwtService.getExpirationTime());
  
          return ResponseEntity.ok(loginResponse);
      }
        
      @GetMapping("/getAllUsers")
      public ResponseEntity<List<User>> getAllUsers() {
          List<User> users = userService.getAllUser();
  
          return ResponseEntity.ok(users);
      }
  }
  
  

Youtube Video –

Github Repository –

https://github.com/amangupta7024/JWTAuthentication-SpringBoot

Explore our more articals

JWT Authentication using Spring Boot 3 & Spring Security

JWT (JSON Web Token) are widely used for authentication in modern web applications. They offer a simple, secure, and stateless way to authenticate users and secure API endpoints, making them an ideal choice for token-based authentication.

Implement JWT Authentication using Spring Boot 3 & Spring Security

JWT Authentication using spring boot 3 & spring security while creating secure API endpoint. JWT token is simple, secure, stateless way to authenticate users and create secure API endpoints, by making them an ideal choice for token-based authentication

8 thoughts on “JWT Authentication using Spring Boot 3 – Implement JWT token using Spring Boot”

Leave a Comment