DEV Community

DevCorner2
DevCorner2

Posted on

Step-by-step guide to implementing JWT Authentication in a Spring Boot application, in a clean, structured, and modular way

Here's a step-by-step guide to implementing JWT Authentication in a Spring Boot application, in a clean, structured, and modular way:


โœ… Technologies Used:

  • Spring Boot 3.x
  • Spring Security
  • JWT (JSON Web Token)
  • Maven or Gradle
  • Java 17+
  • JPA & H2/MySQL (optional)

๐Ÿงฑ Project Structure

src/main/java/com/example/jwtsecurity
โ”‚
โ”œโ”€โ”€ config
โ”‚   โ””โ”€โ”€ SecurityConfig.java
โ”‚
โ”œโ”€โ”€ controller
โ”‚   โ””โ”€โ”€ AuthController.java
โ”‚
โ”œโ”€โ”€ dto
โ”‚   โ”œโ”€โ”€ AuthRequest.java
โ”‚   โ””โ”€โ”€ AuthResponse.java
โ”‚
โ”œโ”€โ”€ entity
โ”‚   โ””โ”€โ”€ User.java
โ”‚
โ”œโ”€โ”€ repository
โ”‚   โ””โ”€โ”€ UserRepository.java
โ”‚
โ”œโ”€โ”€ security
โ”‚   โ”œโ”€โ”€ JwtAuthenticationFilter.java
โ”‚   โ”œโ”€โ”€ JwtTokenProvider.java
โ”‚   โ””โ”€โ”€ CustomUserDetailsService.java
โ”‚
โ”œโ”€โ”€ service
โ”‚   โ””โ”€โ”€ AuthService.java
โ”‚
โ””โ”€โ”€ JwtSecurityApplication.java
Enter fullscreen mode Exit fullscreen mode

1๏ธโƒฃ Add Maven Dependencies (pom.xml)

<dependencies>
    <!-- Spring Boot Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Spring Security -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

    <!-- JWT -->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.1</version>
    </dependency>

    <!-- JPA and H2 (or switch to MySQL) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
    </dependency>
</dependencies>
Enter fullscreen mode Exit fullscreen mode

2๏ธโƒฃ Create User Entity

// entity/User.java
package com.example.jwtsecurity.entity;

import jakarta.persistence.*;
import lombok.*;

@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User {
    @Id
    @GeneratedValue
    private Long id;
    private String username;
    private String password;
    private String role;
}
Enter fullscreen mode Exit fullscreen mode

3๏ธโƒฃ Create UserRepository

// repository/UserRepository.java
package com.example.jwtsecurity.repository;

import com.example.jwtsecurity.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByUsername(String username);
}
Enter fullscreen mode Exit fullscreen mode

4๏ธโƒฃ DTOs

// dto/AuthRequest.java
package com.example.jwtsecurity.dto;

import lombok.*;

@Data
public class AuthRequest {
    private String username;
    private String password;
}
Enter fullscreen mode Exit fullscreen mode
// dto/AuthResponse.java
package com.example.jwtsecurity.dto;

import lombok.*;

@Data
@AllArgsConstructor
public class AuthResponse {
    private String token;
}
Enter fullscreen mode Exit fullscreen mode

5๏ธโƒฃ JwtTokenProvider

// security/JwtTokenProvider.java
package com.example.jwtsecurity.security;

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

import java.util.Date;

@Component
public class JwtTokenProvider {
    private final String JWT_SECRET = "secret_key";
    private final long JWT_EXPIRATION = 604800000L;

    public String generateToken(String username) {
        return Jwts.builder()
                .setSubject(username)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + JWT_EXPIRATION))
                .signWith(SignatureAlgorithm.HS512, JWT_SECRET)
                .compact();
    }

    public String getUsernameFromToken(String token) {
        return Jwts.parser()
                .setSigningKey(JWT_SECRET)
                .parseClaimsJws(token)
                .getBody()
                .getSubject();
    }

    public boolean validateToken(String token) {
        try {
            Jwts.parser().setSigningKey(JWT_SECRET).parseClaimsJws(token);
            return true;
        } catch (JwtException ex) {
            return false;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

6๏ธโƒฃ CustomUserDetailsService

// security/CustomUserDetailsService.java
package com.example.jwtsecurity.security;

import com.example.jwtsecurity.entity.User;
import com.example.jwtsecurity.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.*;
import org.springframework.stereotype.Service;

@Service
public class CustomUserDetailsService implements UserDetailsService {
    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username)
                        .orElseThrow(() -> new UsernameNotFoundException("User not found"));
        return org.springframework.security.core.userdetails.User
                .withUsername(user.getUsername())
                .password(user.getPassword())
                .roles(user.getRole())
                .build();
    }
}
Enter fullscreen mode Exit fullscreen mode

7๏ธโƒฃ JwtAuthenticationFilter

// security/JwtAuthenticationFilter.java
package com.example.jwtsecurity.security;

import jakarta.servlet.*;
import jakarta.servlet.http.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.*;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component
public class JwtAuthenticationFilter extends GenericFilter {

    @Autowired
    private JwtTokenProvider jwtTokenProvider;

    @Autowired
    private CustomUserDetailsService userDetailsService;

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String header = httpRequest.getHeader("Authorization");

        if (header != null && header.startsWith("Bearer ")) {
            String token = header.substring(7);
            if (jwtTokenProvider.validateToken(token)) {
                String username = jwtTokenProvider.getUsernameFromToken(token);
                UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                UsernamePasswordAuthenticationToken authToken =
                        new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                SecurityContextHolder.getContext().setAuthentication(authToken);
            }
        }

        chain.doFilter(request, response);
    }
}
Enter fullscreen mode Exit fullscreen mode

8๏ธโƒฃ SecurityConfig

// config/SecurityConfig.java
package com.example.jwtsecurity.config;

import com.example.jwtsecurity.security.JwtAuthenticationFilter;
import com.example.jwtsecurity.security.CustomUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.*;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.*;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.crypto.bcrypt.*;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class SecurityConfig {

    @Autowired
    private JwtAuthenticationFilter jwtAuthenticationFilter;

    @Autowired
    private CustomUserDetailsService userDetailsService;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.csrf(csrf -> csrf.disable())
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/auth/**").permitAll()
                .anyRequest().authenticated()
            )
            .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }

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

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
Enter fullscreen mode Exit fullscreen mode

9๏ธโƒฃ AuthService

// service/AuthService.java
package com.example.jwtsecurity.service;

import com.example.jwtsecurity.dto.AuthRequest;
import com.example.jwtsecurity.dto.AuthResponse;
import com.example.jwtsecurity.security.JwtTokenProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;

@Service
public class AuthService {
    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtTokenProvider jwtTokenProvider;

    public AuthResponse login(AuthRequest request) {
        Authentication authentication = authenticationManager.authenticate(
            new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword())
        );
        String token = jwtTokenProvider.generateToken(authentication.getName());
        return new AuthResponse(token);
    }
}
Enter fullscreen mode Exit fullscreen mode

๐Ÿ”Ÿ AuthController

// controller/AuthController.java
package com.example.jwtsecurity.controller;

import com.example.jwtsecurity.dto.AuthRequest;
import com.example.jwtsecurity.dto.AuthResponse;
import com.example.jwtsecurity.service.AuthService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/auth")
public class AuthController {

    @Autowired
    private AuthService authService;

    @PostMapping("/login")
    public AuthResponse login(@RequestBody AuthRequest request) {
        return authService.login(request);
    }
}
Enter fullscreen mode Exit fullscreen mode

๐Ÿ”š Main Application

// JwtSecurityApplication.java
package com.example.jwtsecurity;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class JwtSecurityApplication {
    public static void main(String[] args) {
        SpringApplication.run(JwtSecurityApplication.class, args);
    }
}
Enter fullscreen mode Exit fullscreen mode

๐Ÿงช Test

  • Run the app.
  • Hit POST /api/auth/login with:
{
  "username": "admin",
  "password": "admin"
}
Enter fullscreen mode Exit fullscreen mode
  • Use the returned token in the Authorization header: Bearer <TOKEN> for protected endpoints.

Top comments (0)