Implementing JWT authentication in Spring Boot

Java
Spring Boot

May 18, 2025 (2mo ago)

Introduction

In this tutorial, we will learn how to implement JWT authentication and role-based authorization in a Spring Boot application. We will use Spring Security to secure our application and demonstrate how to configure JWT with a simple example.

Pom XML

Make sure starter and resource server dependencies are included in your pom.xml file:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>

Application Properties

In your application.yaml file, configure the OAuth2 resource server properties:

app:
  jwt:
    key: 5pAq6zRyX8bC3dV2wS7gN1mK9jF0hL4tUoP6iBvE3nG8xZaQrY7cW2fA # according to SHA-256 requirements
    algorithm: HS256
    issuer: http://localhost:8080
    expiresIn: 1m # how fast the token expires

Jwt Configuration Service

Create a service class to handle JWT properties and configuration

@Service
@RequiredArgsConstructor
public class JwtPropertiesService {
 
  @Value("${app.jwt.key}")
  private String key;
 
  @Getter
  @Value("${app.jwt.issuer}")
  private String issuer;
 
  @Value("${app.jwt.algorithm}")
  private String algorithm;
 
  @Value("${app.jwt.expiresIn}")
  private String expiresIn;
 
  public JWSAlgorithm getAlgorithm() {
    return JWSAlgorithm.parse(algorithm);
  }
 
  public SecretKey getKey() {
    var jwk = new OctetSequenceKey.Builder(key.getBytes()).algorithm(getAlgorithm()).build();
    return jwk.toSecretKey();
  }
 
  public Duration getExpiresIn() {
    return Duration.parse(expiresIn);
  }
}

Security Configuration

Create a security configuration class to set up the security filter chain and JWT authentication:

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@RequiredArgsConstructor
public class SecurityConfig {
 
  private final JwtPropertiesService jwtPropertiesService;
 
  @Bean
  public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    return http.csrf(AbstractHttpConfigurer::disable)
        .oauth2ResourceServer(configurer -> configurer.jwt(Customizer.withDefaults()))
        .build();
  }
 
  @Bean
  public JwtDecoder jwtDecoder() {
    return NimbusJwtDecoder.withSecretKey(jwtPropertiesService.getKey()).build();
  }
}

Example on creating a JWT token

@Service
@RequiredArgsConstructor
public class JwtGeneratorService {
 
  private final JwtPropertiesService appJwtProperties;
 
  public String generateJWT(Map<String, Object> claims) {
    var key = appJwtProperties.getKey();
    var algorithm = appJwtProperties.getAlgorithm();
 
    var header = new JWSHeader(algorithm);
    var claimsSet = buildClaimsSet(claims);
 
    var jwt = new SignedJWT(header, claimsSet);
 
    try {
      var signer = new MACSigner(key);
      jwt.sign(signer);
    } catch (JOSEException e) {
      throw new RuntimeException("Unable to generate JWT", e);
    }
 
    return jwt.serialize();
  }
 
  private JWTClaimsSet buildClaimsSet(Map<String, Object> claims) {
    var issuer = appJwtProperties.getIssuer();
    var issuedAt = Instant.now();
    var expirationTime = issuedAt.plus(appJwtProperties.getExpiresIn());
 
    var builder =
        new JWTClaimsSet.Builder()
            .issuer(issuer)
            .issueTime(Date.from(issuedAt))
            .expirationTime(Date.from(expirationTime));
 
    claims.forEach(builder::claim);
 
    return builder.build();
  }
}

Token generator controller

@RestController
@RequestMapping("/auth")
@RequiredArgsConstructor
public class AuthController {
 
    private final JwtService jwtService;
 
    @PostMapping(path = "/token", consumes = APPLICATION_JSON_VALUE)
    public String getToken(@RequestBody Map<String, Object> claims) {
        return jwtService.generateJWT(claims);
    }
}

Note: The claims map is used to store user information and roles. An example of claims should look as follows

{
  "sub": "user",
  "name": "John Doe",
  "scope": ["GUEST", "ADMIN"],
}

Using token in the authentication

@RestController
@RequestMapping("/greeting")
public class GreetingController {
 
    @GetMapping
    @PreAuthorize("hasAuthority('SCOPE_GUEST')")
    public String greet(Authentication authentication) {
        return "Hello, %s. You have next permissions: %s"
                .formatted(authentication.getName(), authentication.getAuthorities());
    }
}

Conclusion

Spring Boot and Spring Security does changed a lot in the last years. Make sure to try this out in a simple application before using it in production.