View Javadoc
1   package fr.tiogars.domaintemplate.domains.auth.services;
2   
3   import java.nio.charset.StandardCharsets;
4   import java.time.Clock;
5   import java.time.Instant;
6   import java.util.Date;
7   import java.util.List;
8   import java.util.Optional;
9   
10  import javax.crypto.SecretKey;
11  
12  import org.springframework.beans.factory.annotation.Autowired;
13  import org.springframework.security.core.userdetails.UserDetails;
14  import org.springframework.stereotype.Service;
15  
16  import fr.tiogars.domaintemplate.config.JwtProperties;
17  import io.jsonwebtoken.JwtException;
18  import io.jsonwebtoken.Jwts;
19  import io.jsonwebtoken.security.Keys;
20  
21  /** Generates and validates signed JWT access tokens. */
22  @Service
23  public class JwtTokenService {
24  
25      private final Clock clock;
26      private final JwtProperties properties;
27  
28      @Autowired
29      public JwtTokenService(JwtProperties properties) {
30          this(properties, Clock.systemUTC());
31      }
32  
33      JwtTokenService(JwtProperties properties, Clock clock) {
34          this.properties = properties;
35          this.clock = clock;
36      }
37  
38      public String generateToken(UserDetails userDetails) {
39          Instant now = Instant.now(clock);
40          List<String> authorities = userDetails.getAuthorities().stream()
41              .map(authority -> authority.getAuthority())
42                  .toList();
43          return Jwts.builder()
44                  .subject(userDetails.getUsername())
45                  .issuedAt(Date.from(now))
46                  .expiration(Date.from(expiresAt(now)))
47                  .claim("authorities", authorities)
48                  .signWith(signingKey())
49                  .compact();
50      }
51  
52      public Optional<String> extractUsername(String token) {
53          try {
54              return Optional.ofNullable(Jwts.parser()
55                      .verifyWith(signingKey())
56                      .build()
57                      .parseSignedClaims(token)
58                      .getPayload()
59                      .getSubject());
60          } catch (JwtException | IllegalArgumentException _) {
61              return Optional.empty();
62          }
63      }
64  
65      public boolean isValid(String token, UserDetails userDetails) {
66          return extractUsername(token)
67                  .map(username -> username.equals(userDetails.getUsername()))
68                  .orElse(false);
69      }
70  
71      public Instant expiresAt() {
72          return expiresAt(Instant.now(clock));
73      }
74  
75      private Instant expiresAt(Instant now) {
76          return now.plusSeconds(properties.expirationSeconds());
77      }
78  
79      private SecretKey signingKey() {
80          return Keys.hmacShaKeyFor(properties.secret().getBytes(StandardCharsets.UTF_8));
81      }
82  }