Working with JWT Authentication
1. Understanding JWT Structure
JWT Anatomy
HEADER.PAYLOAD.SIGNATURE
{alg:"HS256",typ:"JWT"} . {sub:"123",iat:…,exp:…} . HMAC-SHA256(...)
| Claim | Meaning |
|---|---|
iss |
Issuer |
sub |
Subject (user id) |
aud |
Audience |
exp |
Expiry (unix sec) |
iat |
Issued at |
nbf |
Not-before |
jti |
Token id (revocation) |
2. Creating JWT Tokens
Example: Issue JWT with claims and expiry
@Service
public class JwtIssuer {
private final JwtEncoder encoder;
public JwtIssuer(JwtEncoder encoder) { this.encoder = encoder; }
public String issue(String subject, Set<String> roles) {
Instant now = Instant.now();
JwtClaimsSet claims = JwtClaimsSet.builder()
.issuer("https://auth.example.com")
.issuedAt(now).expiresAt(now.plus(15, ChronoUnit.MINUTES))
.subject(subject).claim("roles", roles)
.build();
return encoder.encode(JwtEncoderParameters.from(claims)).getTokenValue();
}
}
3. Validating and Parsing JWT Tokens
Example: JWT decoder with secret key
@Bean
JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withSecretKey(secretKey).build(); // HS256
}
// Auto-validation: signature, exp, nbf, iss
4. Implementing JWT Authentication Filter
| Approach | Recommendation |
|---|---|
| Spring Security OAuth2 Resource Server | Preferred — handles filter, validation, cache |
Custom OncePerRequestFilter |
Only when special token format needed |
Example: Configure JWT resource server
http.oauth2ResourceServer(o -> o.jwt(j -> j.decoder(jwtDecoder())
.jwtAuthenticationConverter(converter())));
5. Configuring Token Expiration and Refresh
| Token | Typical TTL |
|---|---|
| Access | 5–15 minutes |
| Refresh | 7–30 days, rotated |
| ID token (OIDC) | Same as access |
6. Storing User Details in JWT Claims
Example: Map roles claim to authorities
@Bean
JwtAuthenticationConverter converter() {
JwtGrantedAuthoritiesConverter ga = new JwtGrantedAuthoritiesConverter();
ga.setAuthoritiesClaimName("roles");
ga.setAuthorityPrefix("ROLE_");
JwtAuthenticationConverter c = new JwtAuthenticationConverter();
c.setJwtGrantedAuthoritiesConverter(ga);
return c;
}
7. Handling Token-Based Authorization
Example: Access JWT claims in controller
@RestController
public class MeController {
@GetMapping("/me")
public Map<String,Object> me(@AuthenticationPrincipal Jwt jwt) {
return Map.of("sub", jwt.getSubject(), "roles", jwt.getClaim("roles"));
}
}
8. Implementing Refresh Token Mechanism
Refresh Token Rotation
- Login → issue access (15m) + refresh (7d, store hash)
- Client calls
/auth/refreshwith refresh token - Server validates, revokes used refresh, issues new pair
- Detect reuse → revoke entire family (token theft)
9. Securing JWT Secret Keys
| Practice | Reason |
|---|---|
| Min 256-bit secret (HS256) | Required for HMAC strength |
| Store in vault / secret manager | Never in source / config |
| Rotate periodically | Limit blast radius |
| Use kid header | Support multiple keys for rotation |
10. Handling Token Revocation Strategies
| Strategy | Trade-off |
|---|---|
| Short TTL (no blacklist) | Simple; brief window of misuse |
| Redis blacklist by jti | Strong revocation; extra lookup |
| Refresh token DB | Revoke long-lived tokens |
| Token versioning per user | Bump version → all tokens invalid |