Table of contents
1.
Introduction
2.
Demo Project
3.
Steps to Set Up the Project
4.
Creating a Basic Spring Application
4.1.
Step 1: Add Dependencies in pom.xml
4.2.
Step 2: Configure Application Properties
5.
Spring Security Architecture Overview
5.1.
1. Authentication Manager
5.2.
2. Authentication Provider
5.3.
3. UserDetailsService
5.4.
4. JWT Token Provider
6.
Implementing JWT Authentication
6.1.
Step 1: Create a User Model
6.2.
Step 2: Create a JWT Utility Class
6.3.
Step 3: Create a JWT Authentication Filter
6.4.
Step 4: Configure Spring Security
7.
Spring Security Filters Chain
8.
Testing JWT Authentication
9.
Authentication Using JWT with Spring Security
10.
JWT Authorization with Spring Security
11.
Frequently Asked Questions
11.1.
What is the purpose of JWT in authentication?
11.2.
How does Spring Security integrate with JWT?
11.3.
Can JWT authentication be used with role-based access control?
12.
Conclusion
Last Updated: Feb 3, 2025
Easy

Implement JWT authentication in a Spring Boot

Career growth poll
Do you think IIT Guwahati certified course can help you in your career?

Introduction

JWT (JSON Web Token) authentication in Spring Boot is a secure way to manage user authentication in APIs. It generates a token after login, which the client uses for future requests. This method ensures stateless authentication, improving security and performance.

In this article, we will explore how to implement JWT authentication in a Spring Boot application. By the end, you will understand the authentication flow, how JWT works, and how Spring Security integrates with it.

Demo Project

To understand JWT authentication in Spring Boot, we will create a simple project that includes:

  • A Spring Boot application
     
  • User authentication with JWT
     
  • Securing API endpoints using JWT

Steps to Set Up the Project

  1. Create a new Spring Boot application using Spring Initializr.
     
  2. Add dependencies for Spring Security, Spring Web, and JWT.
     
  3. Implement user authentication using JWT.
     
  4. Protect API endpoints with JWT authentication.
     
  5. Test the implementation.

Creating a Basic Spring Application

To get started, create a Spring Boot application with the necessary dependencies:

Step 1: Add Dependencies in pom.xml

<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Spring Boot Starter Security -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
        <!-- JSON Web Token -->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.11.2</version>
    </dependency>
</dependencies>

Step 2: Configure Application Properties

In application.properties, configure your JWT secret key and other settings:

server.port=8080
spring.security.user.name=admin
spring.security.user.password=admin
jwt.secret=yourSecretKey
jwt.expiration=3600000

Spring Security Architecture Overview

Spring Security is responsible for handling authentication and authorization in Spring Boot applications. The main components involved are:

1. Authentication Manager

It is responsible for verifying user credentials and generating authentication tokens.

2. Authentication Provider

It checks user credentials against a data source (e.g., database, in-memory, etc.).

3. UserDetailsService

It loads user details from a data source and is used during authentication.

4. JWT Token Provider

It generates and validates JWT tokens.

Implementing JWT Authentication

Step 1: Create a User Model

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

Step 2: Create a JWT Utility Class

import io.jsonwebtoken.*;
import java.util.Date;
import io.jsonwebtoken.security.Keys;
import java.security.Key;

public class JwtUtil {
    private static final String SECRET_KEY = "yourSecretKey";
    private static final long EXPIRATION_TIME = 3600000;
    
    public static String generateToken(String username) {
        return Jwts.builder()
                .setSubject(username)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
                .signWith(Keys.hmacShaKeyFor(SECRET_KEY.getBytes()), SignatureAlgorithm.HS256)
                .compact();
    }
    
    public static String extractUsername(String token) {
        return Jwts.parserBuilder()
                .setSigningKey(Keys.hmacShaKeyFor(SECRET_KEY.getBytes()))
                .build()
                .parseClaimsJws(token)
                .getBody()
                .getSubject();
    }
}

Step 3: Create a JWT Authentication Filter

import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class JwtFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        String token = request.getHeader("Authorization");
        if (token != null && token.startsWith("Bearer ")) {
            String username = JwtUtil.extractUsername(token.substring(7));
            if (username != null) {
                SecurityContextHolder.getContext().setAuthentication(new UserAuthentication(username));
            }
        }
        chain.doFilter(request, response);
    }
}

Step 4: Configure Spring Security

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeHttpRequests()
            .requestMatchers("/login").permitAll()
            .anyRequest().authenticated()
            .and()
            .addFilterBefore(new JwtFilter(), UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }
}

Spring Security Filters Chain

Spring Security applies a chain of filters to incoming requests. Some of the important filters include:

  1. UsernamePasswordAuthenticationFilter – Handles form-based login.
     
  2. BasicAuthenticationFilter – Handles HTTP Basic authentication.
     
  3. BearerTokenAuthenticationFilter – Extracts JWT tokens from requests.
     
  4. SecurityContextPersistenceFilter – Stores authentication information in a session.

Testing JWT Authentication

Run your Spring Boot application and use Postman to test authentication:

  1. Login to get JWT Token:

    • Endpoint: POST /login
       
    • Body: { "username": "admin", "password": "admin" }
       
    • Response: { "token": "eyJhbGciOiJIUzI1NiIsIn..." }
       
  2. Access Protected API:

    • Endpoint: GET /protected
       
    • Headers: Authorization: Bearer <JWT_TOKEN>
       
    • Response: Welcome, admin!

Authentication Using JWT with Spring Security

JWT, or JSON Web Token, is a compact and independent way to securely transmit information between parties. It works by encoding the data in a JSON object that can be verified and trusted because it's digitally signed. When you use JWT for authentication in a Spring Boot application, you're essentially creating a system where users can log in once and then use a token to access protected resources without needing to log in again.

To set up JWT authentication in a Spring Boot application, you need to follow these steps:

Add Dependencies: First, you need to add the necessary dependencies to your pom.xml file. This includes Spring Security and a library to handle JWT operations.

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.1</version>
    </dependency>
</dependencies>


Configure Spring Security: Next, you need to configure Spring Security to use JWT for authentication. This involves creating a configuration class that extends WebSecurityConfigurerAdapter.

import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeRequests()
            .antMatchers("/api/auth/**").permitAll()
            .anyRequest().authenticated()
            .and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }
}


Create JWT Utility Class: You need a utility class to handle the creation and validation of JWT tokens.

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;

public class JwtUtil {
    private String secretKey = "secret";

    public String generateToken(String username) {
        return Jwts.builder()
                .setSubject(username)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // 10 hours
                .signWith(SignatureAlgorithm.HS256, secretKey)
                .compact();
    }

    public String extractUsername(String token) {
        return extractClaim(token, Claims::getSubject);
    }

    public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }

    private Claims extractAllClaims(String token) {
        return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody();
    }

    public Boolean isTokenExpired(String token) {
        final Date expiration = extractExpiration(token);
        return expiration.before(new Date());
    }

    private Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }
}


Create Authentication Filter: You need a filter to validate the JWT token for each request.

import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class JwtRequestFilter extends UsernamePasswordAuthenticationFilter {
    private JwtUtil jwtUtil;
    public JwtRequestFilter(JwtUtil jwtUtil) {
        this.jwtUtil = jwtUtil;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        final String authorizationHeader = request.getHeader("Authorization");

        String username = null;
        String jwt = null;

        if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
            jwt = authorizationHeader.substring(7);
            username = jwtUtil.extractUsername(jwt);
        }

        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);


            if (jwtUtil.isTokenValid(jwt, userDetails)) {
                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                usernamePasswordAuthenticationToken
                        .setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
            }
        }
        chain.doFilter(request, response);
    }
}


Create Authentication Controller: Finally, you need a controller to handle user authentication and token generation.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.*;

@RestController
public class AuthenticationController {
    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtUtil jwtTokenUtil;

    @Autowired
    private UserDetailsService userDetailsService;

    @PostMapping("/api/authenticate")
    public ResponseEntity<?> createAuthenticationToken(@RequestBody AuthenticationRequest authenticationRequest) throws Exception {
        try {
            authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword())
            );
        } catch (BadCredentialsException e) {
            throw new Exception("Incorrect username or password", e);
        }

        final UserDetails userDetails = userDetailsService
                .loadUserByUsername(authenticationRequest.getUsername());

        final String jwt = jwtTokenUtil.generateToken(userDetails);

        return ResponseEntity.ok(new AuthenticationResponse(jwt));
    }
}


This covers the basic setup for JWT authentication in a Spring Boot application. We've added the necessary dependencies, configured Spring Security, created a JWT utility class, an authentication filter, and an authentication controller. These components work together to handle user authentication and token management.

JWT Authorization with Spring Security

Now that we have set up JWT authentication, let's move on to how we can use JWT for authorization in our Spring Boot application. Authorization is the process of determining whether a user has the necessary permissions to perform a specific action. With JWT, we can include roles and permissions in the token, which can then be used to control access to different parts of our application.

To implement JWT authorization, we need to:

Modify the JWT Utility Class: We need to update our JwtUtil class to include roles in the JWT token.

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class JwtUtil {
    private String secretKey = "secret";

    public String generateToken(String username, List<String> roles) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("roles", roles);


        return Jwts.builder()
                .setClaims(claims)
                .setSubject(username)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // 10 hours
                .signWith(SignatureAlgorithm.HS256, secretKey)
                .compact();
    }

    public String extractUsername(String token) {
        return extractClaim(token, Claims::getSubject);
    }

    public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }

    private Claims extractAllClaims(String token) {
        return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody();
    }

    public Boolean isTokenExpired(String token) {
        final Date expiration = extractExpiration(token);
        return expiration.before(new Date());
    }

    private Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }

    public List<String> extractRoles(String token) {
        return extractClaim(token, claims -> (List<String>) claims.get("roles"));
    }
}


Update the Authentication Controller: We need to update our AuthenticationController to include roles when generating the JWT token.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.*;

@RestController
public class AuthenticationController {
    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtUtil jwtTokenUtil;

    @Autowired
    private UserDetailsService userDetailsService;


    @PostMapping("/api/authenticate")
    public ResponseEntity<?> createAuthenticationToken(@RequestBody AuthenticationRequest authenticationRequest) throws Exception {
        try {
            authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword())
            );
        } catch (BadCredentialsException e) {
            throw new Exception("Incorrect username or password", e);
        }

        final UserDetails userDetails = userDetailsService
                .loadUserByUsername(authenticationRequest.getUsername());


        final String jwt = jwtTokenUtil.generateToken(userDetails.getUsername(), userDetails.getAuthorities().stream()
                .map(GrantedAuthority::getAuthority)
                .collect(Collectors.toList()));

        return ResponseEntity.ok(new AuthenticationResponse(jwt));
    }
}


Configure Role-Based Access Control: We need to configure Spring Security to use the roles in the JWT token to control access to different endpoints.

import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeRequests()
            .antMatchers("/api/auth/**").permitAll()
            .antMatchers("/api/admin/**").hasRole("ADMIN")
            .antMatchers("/api/user/**").hasAnyRole("ADMIN", "USER")
            .anyRequest().authenticated()
            .and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }
}


Create Custom UserDetailsService: We need a custom UserDetailsService to load user details and roles from the database.

import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.core.userdetails.UserDetails;

public class CustomUserDetailsService implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // Load user details from your data source
        User user = userRepository.findByUsername(username)
                .orElseThrow(() -> new UsernameNotFoundException("User not found with username: " + username));

        return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), user.getAuthorities());
    }
}


Create User and Role Entities: We need to create entities for users and roles.

import javax.persistence.*;
import java.util.Set;
@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;


    private String username;
    private String password;


    @ManyToMany(fetch = FetchType.EAGER)
    private Set<Role> roles;


    // Getters and setters
}
@Entity
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    // Getters and setters
}


This setup allows you to control access to different parts of your application based on the roles included in the JWT token. By configuring Spring Security to use these roles, you can ensure that only authorized users can access specific endpoints.

Frequently Asked Questions

What is the purpose of JWT in authentication?

JWT is used to securely transmit user authentication information between client and server without maintaining session state.

How does Spring Security integrate with JWT?

Spring Security filters JWT tokens, verifies their validity, and authenticates users based on token data.

Can JWT authentication be used with role-based access control?

Yes, you can include user roles in JWT claims and implement role-based authorization.

Conclusion

In this article, we implemented JWT authentication in Spring Boot using Spring Security. We learned about the authentication flow, Spring Security filters, and tested the implementation. This approach ensures secure authentication in modern web applications.

Live masterclass