Issuing a JWT

Spring Security · lesson 12 of 31 · 4 min read

Mint signed tokens with the built-in Nimbus encoder and choose sane claims and lifetimes.

Open this lesson in the learning hub

Key points

  • A JWT is signed, not encrypted. Anyone can base64-decode the payload, so never put secrets in claims.
  • Keep them short-lived: 5-15 minutes. You cannot revoke a JWT, you can only outlive it. Pair it with a server-side refresh token.
  • Sign with RSA or EC (RS256) when other services verify your tokens. A shared HMAC secret only works inside one trust boundary.
  • Use NimbusJwtEncoder from spring-security-oauth2-jose. Do not hand-roll base64 and HMAC.
  • Include only what the API needs: subject, issuer, expiry, and the authorities used for authorization.

Example

@Bean
JwtEncoder jwtEncoder(RSAPublicKey publicKey, RSAPrivateKey privateKey) {
    JWK jwk = new RSAKey.Builder(publicKey).privateKey(privateKey).build();
    return new NimbusJwtEncoder(new ImmutableJWKSet<>(new JWKSet(jwk)));
}

public String issue(Authentication auth) {
    Instant now = Instant.now();
    String roles = auth.getAuthorities().stream()
        .map(GrantedAuthority::getAuthority)
        .collect(Collectors.joining(" "));

    JwtClaimsSet claims = JwtClaimsSet.builder()
        .issuer("https://javacodinghub.com")
        .issuedAt(now)
        .expiresAt(now.plus(15, ChronoUnit.MINUTES))
        .subject(auth.getName())
        .claim("roles", roles)
        .build();

    return jwtEncoder.encode(JwtEncoderParameters.from(claims)).getTokenValue();
}

Short expiry, no secrets in the payload, and let Nimbus do the signing.

This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Spring Security course, and every lesson in it is listed on the Spring Security contents page.