JWT Spring Boot Starter

A Spring Boot starter that provides a simple way to implement of JWT-based authentication in your applications.

Features

Getting Started

Installation

Add the dependency to your pom.xml:

<dependency>
    <groupId>io.github.seydoucisse</groupId>
    <artifactId>jwt-spring-boot-starter</artifactId>
    <version>0.1.0</version>
</dependency>

Configuration

  1. Add required properties in application.properties or application.yml:

    • jwt.secret: Your JWT signing key
    • jwt.issuer: Your application name
    jwt:
        secret: your_secret_key
        issuer: your-app-issuer
  2. Implement UserDetailsService:

    • Create a service class that implements UserDetailsService
    • Override loadUserByUsername() method
    • Connect to your user data source
    @Service
    public class MyUserDetailsService implements UserDetailsService {
        
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            // Load user from your database or other source
            // Return a UserDetails implementation
            // Throw UsernameNotFoundException if user not found
        }
    }
  3. Optional: Customize the configuration

    • Override default JWT properties
    • Implement custom security configuration
    • Add custom token blacklist service

⚠️ WARNING: You should define these two beans in a configuration class: AuthenticationManager and PasswordEncoder. For example:

@Configuration
public class SecurityConfig {
    
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

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

For detailed configuration options, see the Configuration Properties section below. For customization options, see the Advanced Usage section.

Configuration Properties

The starter can be configured with the following properties:

Core Components

JwtTokenService

The JwtTokenService provides methods for JWT token operations:

TokenBlacklistService

The TokenBlacklistService manages revoked tokens:

By default, an in-memory implementation is provided, but you can create your own implementation by implementing the TokenBlacklistService interface.

Spring Security Integration

The starter automatically configures Spring Security to use JWT authentication. To customize the security configuration, you can extend the provided classes or create your own configuration.

Example Usage

Here's a simple example of how to use the JWT starter in your application:

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

    private final JwtTokenService jwtTokenService;
    private final AuthenticationManager authenticationManager;

    public AuthController(JwtTokenService jwtTokenService, AuthenticationManager authenticationManager) {
        this.jwtTokenService = jwtTokenService;
        this.authenticationManager = authenticationManager;
    }

    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) {
        Authentication authentication = authenticationManager.authenticate(
            new UsernamePasswordAuthenticationToken(
                loginRequest.getUsername(),
                loginRequest.getPassword()
            )
        );
        
        SecurityContextHolder.getContext().setAuthentication(authentication);
        
        // Generate JWT token
        Map<String, Object> claims = new HashMap<>();
        claims.put("roles", authentication.getAuthorities().stream()
            .map(GrantedAuthority::getAuthority)
            .collect(Collectors.toList()));
            
        JwtToken token = jwtTokenService.generateToken(authentication.getName(), claims);
        
        return ResponseEntity.ok(new JwtResponse(token.getToken()));
    }
    
    @PostMapping("/refresh")
    public ResponseEntity<?> refreshToken(@RequestBody TokenRefreshRequest request) {
        JwtToken refreshedToken = jwtTokenService.refreshToken(request.getToken());
        return ResponseEntity.ok(new JwtResponse(refreshedToken.getToken()));
    }

    @PostMapping("/logout")
    public ResponseEntity<?> logout(@RequestBody LogoutRequest request) {
        jwtTokenService.invalidateToken(request.getToken());
        return ResponseEntity.noContent().build();
    }
}

Exception Handling

The starter provides custom exceptions for JWT-related errors:

Advanced Usage

Custom JWT Token Service

You can provide your own implementation of JwtTokenService to customize token generation and validation:

@Service
public class CustomJwtTokenService implements JwtTokenService {
    // Your custom implementation
}

Custom Security Configuration

If you need more control over the security configuration, you can define your own SecurityFilterChain bean:

@Configuration
public class SecurityConfig {
    private final JwtAuthenticationFilter jwtAuthenticationFilter;

    public SecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter) {
        this.jwtAuthenticationFilter = jwtAuthenticationFilter;
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http
                .csrf(AbstractHttpConfigurer::disable)
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/api/public/**").permitAll()
                        .anyRequest().authenticated())
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
                .build();
    }
}

Custom Token Blacklist Implementation

By default, an in-memory implementation of TokenBlacklistService is provided. For production environments, you might want to implement a persistent solution:

@Service
@Primary
public class JpaTokenBlacklistService implements TokenBlacklistService {
    
    private final BlacklistedTokenRepository tokenRepository;
    
    public JpaTokenBlacklistService(BlacklistedTokenRepository tokenRepository) {
        this.tokenRepository = tokenRepository;
    }
    
    @Override
    public void blacklistToken(String token, long expirationDuration) {
        BlacklistedToken blacklistedToken = new BlacklistedToken();
        blacklistedToken.setToken(token);
        blacklistedToken.setExpirationTime(expirationDuration);
        tokenRepository.save(blacklistedToken);
    }
    
    @Override
    public boolean isBlacklisted(String token) {
        return tokenRepository.existsByToken(token);
    }
    
    // Optional: Cleanup method to remove expired tokens
    @Scheduled(fixedRateString = "${jwt.blacklisted-cleanup-interval-ms:600000}")
    public void cleanupExpiredTokens() {
        tokenRepository.deleteByExpirationTimeLessThan(System.currentTimeMillis());
    }
}

Custom Claims

You can add custom claims to your JWT tokens:

Map<String, Object> claims = new HashMap<>();
claims.put("userId", user.getId());
claims.put("email", user.getEmail());
claims.put("roles", user.getRoles());

JwtToken token = jwtTokenService.generateToken(user.getUsername(), claims);

Documentation

For detailed API documentation, please refer to the Javadoc.

License

This project is licensed under the MIT License - see the LICENSE file for details.