Building a JWT Authentication System with Spring Boot

Mayur PatilMayur Patil
4 min read

Authentication is a critical component of modern web applications. In this blog, weโ€™ll walk through creating a JWT (JSON Web Token) Authentication System using Spring Boot. This project demonstrates how to secure RESTful APIs with Spring Security and JWT.

By the end of this blog, youโ€™ll have a fully functional authentication system, complete with:

  • User registration.

  • Role-based access control (e.g., ADMIN, USER).

  • Token-based authentication using JWT.


๐Ÿ”Ž 1. Introduction: What are we building and why?

This project is a JWT Authentication System that utilizes:

  • Spring Security for securing endpoints.

  • JWT (JSON Web Tokens) for stateless authentication.

  • MySQL Database to store user credentials.

Key features:

  • User Registration: Add new users with encrypted passwords.

  • Authentication: Validate user credentials to issue JWT tokens.

  • Role-Based Access: Restrict access based on roles like ADMIN and USER.

  • Token Validation: Validate tokens for secure access to protected endpoints.

This project is perfect for:

  • Learning Spring Security and JWT integration.

  • Implementing role-based access control in RESTful APIs.

  • Building secure, stateless authentication systems.


๐Ÿ“š 2. Backend Architecture

MyUser Entity

The MyUser class represents the user entity stored in the database. It includes fields for:

  • Username and Password.

  • Role for access control.

@Entity
public class MyUser {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String username;
    private String password;
    private String role; // Eg: ADMIN, USER

    // Getters and Setters...
}

MyUserRepository

The MyUserRepository extends JpaRepository and provides a method to find users by their username.

@Repository
public interface MyUserRepository extends JpaRepository<MyUser, Long> {
    Optional<MyUser> findByUsername(String username);
}

MyUserDetailService

The MyUserDetailService implements UserDetailsService to load user details from the database for Spring Security.

@Service
public class MyUserDetailService implements UserDetailsService {

    @Autowired
    private MyUserRepository repository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Optional<MyUser> user = repository.findByUsername(username);
        if (user.isPresent()) {
            var userObj = user.get();
            return User.builder()
                    .username(userObj.getUsername())
                    .password(userObj.getPassword())
                    .roles(userObj.getRole().split(","))
                    .build();
        } else {
            throw new UsernameNotFoundException(username);
        }
    }
}

JwtService

The JwtService handles JWT generation, validation, and extraction of user information.

@Service
public class JwtService {

    private static final String SECRET = "YOUR_SECRET_KEY_HERE";
    private static final long VALIDITY = TimeUnit.MINUTES.toMillis(30);

    public String generateToken(UserDetails userDetails) {
        Map<String, String> claims = new HashMap<>();
        claims.put("iss", "https://secure.genuinecoder.com");
        return Jwts.builder()
                .claims(claims)
                .subject(userDetails.getUsername())
                .issuedAt(Date.from(Instant.now()))
                .expiration(Date.from(Instant.now().plusMillis(VALIDITY)))
                .signWith(Keys.hmacShaKeyFor(Base64.getDecoder().decode(SECRET)))
                .compact();
    }

    public String extractUsername(String jwt) {
        Claims claims = Jwts.parserBuilder()
                .setSigningKey(Keys.hmacShaKeyFor(Base64.getDecoder().decode(SECRET)))
                .build()
                .parseClaimsJws(jwt)
                .getBody();
        return claims.getSubject();
    }

    public boolean isTokenValid(String jwt) {
        Claims claims = Jwts.parserBuilder()
                .setSigningKey(Keys.hmacShaKeyFor(Base64.getDecoder().decode(SECRET)))
                .build()
                .parseClaimsJws(jwt)
                .getBody();
        return claims.getExpiration().after(Date.from(Instant.now()));
    }
}

ContentController

The ContentController exposes endpoints for:

  • Home: Public access.

  • Admin Home: Restricted to ADMIN.

  • User Home: Restricted to USER.

  • Authentication: Validate credentials and return a JWT token.

@RestController
public class ContentController {

    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private JwtService jwtService;
    @Autowired
    private MyUserDetailService myUserDetailService;

    @GetMapping("/home")
    public String handleWelcome() {
        return "Welcome to home!";
    }

    @GetMapping("/admin/home")
    public String handleAdminHome() {
        return "Welcome to ADMIN home!";
    }

    @GetMapping("/user/home")
    public String handleUserHome() {
        return "Welcome to USER home!";
    }

    @PostMapping("/authenticate")
    public String authenticateAndGetToken(@RequestBody LoginForm loginForm) {
        Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(
                loginForm.username(), loginForm.password()
        ));
        if (authentication.isAuthenticated()) {
            return jwtService.generateToken(myUserDetailService.loadUserByUsername(loginForm.username()));
        } else {
            throw new UsernameNotFoundException("Invalid credentials");
        }
    }
}

SecurityConfiguration

The SecurityConfiguration class configures Spring Security:

  • Defines public and protected routes.

  • Associates roles (ADMIN and USER) with specific endpoints.

  • Adds the JwtAuthenticationFilter for token validation.

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Autowired
    private MyUserDetailService userDetailService;
    @Autowired
    private JwtAuthenticationFilter jwtAuthenticationFilter;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity
                .csrf(AbstractHttpConfigurer::disable)
                .authorizeHttpRequests(registry -> {
                    registry.requestMatchers("/home", "/register/**", "/authenticate").permitAll();
                    registry.requestMatchers("/admin/**").hasRole("ADMIN");
                    registry.requestMatchers("/user/**").hasRole("USER");
                    registry.anyRequest().authenticated();
                })
                .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
                .build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

๐Ÿƒโ€โ™‚๏ธ 3. How to Run It

Prerequisites:

  • Java 21 or later.

  • MySQL Database running locally.

Steps:

  1. Clone the GitHub repository:

     git clone https://github.com/Mayurdpatil67/springboot-security-authenticator.git
     cd springboot-security-authenticator
    
  2. Configure the database in application.properties:

     spring.datasource.url=jdbc:mysql://localhost:3306/jwtdb
     spring.datasource.username=root
     spring.datasource.password=yourpassword
    
  3. Run the Spring Boot application:

     ./gradlew bootRun
    
  4. Test the API endpoints using tools like Postman or curl.


๐ŸŒŸ 4. GitHub Repository

The complete source code for this project is available on GitHub. Feel free to clone, fork, or contribute to the repository:

GitHub Link: JWT Authentication System


๐ŸŒŸ 5. Conclusion

This project demonstrates how to implement a secure JWT authentication system with Spring Boot. Youโ€™ve learned:

  • How to configure Spring Security for role-based access control.

  • How to use JWT for stateless authentication.

  • How to secure RESTful APIs with Spring Boot.

Whatโ€™s Next?

  • Add refresh tokens for better token management.

  • Implement logout functionality by revoking tokens.

  • Extend the system to include OAuth2 for third-party authentication.

Happy coding! ๐Ÿš€๐Ÿ”‘

0
Subscribe to my newsletter

Read articles from Mayur Patil directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Mayur Patil
Mayur Patil

Skilled in Java & Spring Boot , Backedn Enthusiast