2828import cn .org .codecrafters .simplejwt .constants .TokenAlgorithm ;
2929import cn .org .codecrafters .simplejwt .exceptions .WeakSecretException ;
3030import cn .org .codecrafters .simplejwt .jjwt .config .JjwtTokenResolverConfig ;
31- import com .fasterxml .jackson .core .type .TypeReference ;
3231import io .jsonwebtoken .Claims ;
3332import io .jsonwebtoken .Jws ;
3433import io .jsonwebtoken .Jwts ;
35- import io .jsonwebtoken .SignatureAlgorithm ;
3634import io .jsonwebtoken .security .Keys ;
35+ import io .jsonwebtoken .security .SecureDigestAlgorithm ;
3736import lombok .extern .slf4j .Slf4j ;
3837
38+ import javax .crypto .SecretKey ;
3939import java .lang .reflect .InvocationTargetException ;
4040import java .nio .charset .StandardCharsets ;
41- import java .security .Key ;
4241import java .time .Duration ;
4342import java .time .LocalDateTime ;
4443import java .time .ZoneId ;
4544import java .util .*;
4645
4746/**
4847 * The {@link JjwtTokenResolver} class is an implementation of the {@link
49- * cn.org.codecrafters.simplejwt. TokenResolver} interface. It uses the {@code
48+ * TokenResolver} interface. It uses the {@code
5049 * io.jsonwebtoken:jjwt} library to handle JSON Web Token (JWT) resolution.
5150 * This resolver provides functionality to create, extract, verify, and renew
5251 * JWT tokens using various algorithms and custom payload data.
9291 * @see Claims
9392 * @see Jws
9493 * @see Jwts
95- * @see SignatureAlgorithm
94+ * @see SecureDigestAlgorithm
9695 * @see Keys
9796 * @since 1.0.0
9897 */
@@ -101,14 +100,21 @@ public class JjwtTokenResolver implements TokenResolver<Jws<Claims>> {
101100
102101 private final GuidCreator <?> jtiCreator ;
103102
104- private final SignatureAlgorithm algorithm ;
103+ private final SecureDigestAlgorithm < SecretKey , SecretKey > algorithm ;
105104
106105 private final String issuer ;
107106
108- private final Key key ;
107+ private final SecretKey key ;
109108
110109 private final JjwtTokenResolverConfig config = JjwtTokenResolverConfig .getInstance ();
111110
111+ /**
112+ * Create a resolver with specified algorithm, issuer, secret and guid strategy.
113+ *
114+ * @param algorithm specified algorithm
115+ * @param issuer specified issuer
116+ * @param secret specified secret
117+ */
112118 public JjwtTokenResolver (GuidCreator <?> jtiCreator , TokenAlgorithm algorithm , String issuer , String secret ) {
113119 if (Objects .isNull (secret ) || secret .isBlank ()) {
114120 throw new IllegalArgumentException ("A secret is required to build a JSON Web Token." );
@@ -129,6 +135,13 @@ public JjwtTokenResolver(GuidCreator<?> jtiCreator, TokenAlgorithm algorithm, St
129135 this .key = Keys .hmacShaKeyFor (secret .getBytes (StandardCharsets .UTF_8 ));
130136 }
131137
138+ /**
139+ * Create a resolver with specified algorithm, issuer, secret and default guid strategy.
140+ *
141+ * @param algorithm specified algorithm
142+ * @param issuer specified issuer
143+ * @param secret specified secret
144+ */
132145 public JjwtTokenResolver (TokenAlgorithm algorithm , String issuer , String secret ) {
133146 if (secret == null || secret .isBlank ()) {
134147 throw new IllegalArgumentException ("A secret is required to build a JSON Web Token." );
@@ -149,6 +162,13 @@ public JjwtTokenResolver(TokenAlgorithm algorithm, String issuer, String secret)
149162 this .key = Keys .hmacShaKeyFor (secret .getBytes (StandardCharsets .UTF_8 ));
150163 }
151164
165+ /**
166+ * Create a resolver with specified issuer, secret, default algorithm and guid strategy.
167+ *
168+ * @param issuer specified issuer
169+ * @param secret specified secret
170+ * @see #JjwtTokenResolver(TokenAlgorithm, String, String)
171+ */
152172 public JjwtTokenResolver (String issuer , String secret ) {
153173 if (secret == null || secret .isBlank ()) {
154174 throw new IllegalArgumentException ("A secret is required to build a JSON Web Token." );
@@ -169,33 +189,19 @@ public JjwtTokenResolver(String issuer, String secret) {
169189 this .key = Keys .hmacShaKeyFor (secret .getBytes (StandardCharsets .UTF_8 ));
170190 }
171191
192+ /**
193+ * Create a resolver with specified issuer, random secret string, default algorithm and guid strategy.
194+ *
195+ * @param issuer specified issuer
196+ * @see #JjwtTokenResolver(String, String)
197+ */
172198 public JjwtTokenResolver (String issuer ) {
173199 this .jtiCreator = UUID ::randomUUID ;
174200 this .algorithm = config .getAlgorithm (TokenAlgorithm .HS256 );
175201 this .issuer = issuer ;
176202 this .key = Keys .hmacShaKeyFor (SecretCreator .createSecret (32 , true , true , true ).getBytes (StandardCharsets .UTF_8 ));
177203 }
178204
179- private String buildToken (Duration expireAfter , String audience , String subject , Map <String , Object > claims ) {
180- var now = LocalDateTime .now ();
181- var builder = Jwts .builder ()
182- .setHeaderParam ("typ" , "JWT" )
183- .setIssuedAt (Date .from (now .atZone (ZoneId .systemDefault ()).toInstant ()))
184- .setNotBefore (Date .from (now .atZone (ZoneId .systemDefault ()).toInstant ()))
185- .setExpiration (Date .from (now .plus (expireAfter ).atZone (ZoneId .systemDefault ()).toInstant ()))
186- .setSubject (subject )
187- .setAudience (audience )
188- .setIssuer (this .issuer )
189- .setId (jtiCreator .nextId ().toString ());
190-
191- if (claims != null && !claims .isEmpty ()) {
192- builder .addClaims (claims );
193- }
194-
195- return builder .signWith (key , algorithm )
196- .compact ();
197- }
198-
199205 /**
200206 * Creates a new token with the specified expiration time, subject, and
201207 * audience.
@@ -280,10 +286,10 @@ public <T extends TokenPayload> String createToken(Duration expireAfter, String
280286 */
281287 @ Override
282288 public Jws <Claims > resolve (String token ) {
283- return Jwts .parserBuilder ()
284- .setSigningKey (key )
289+ return Jwts .parser ()
290+ .verifyWith (key )
285291 .build ()
286- .parseClaimsJws (token );
292+ .parseSignedClaims (token );
287293 }
288294
289295 /**
@@ -300,7 +306,7 @@ public Jws<Claims> resolve(String token) {
300306 public <T extends TokenPayload > T extract (String token , Class <T > targetType ) {
301307 var resolvedToken = resolve (token );
302308
303- var claims = resolvedToken .getBody ();
309+ var claims = resolvedToken .getPayload ();
304310 try {
305311 var bean = targetType .getConstructor ().newInstance ();
306312
@@ -351,9 +357,9 @@ public <T extends TokenPayload> T extract(String token, Class<T> targetType) {
351357 @ Override
352358 public String renew (String oldToken , Duration expireAfter ) {
353359 var resolvedToken = resolve (oldToken );
354- var tokenPayloads = resolvedToken .getBody ();
360+ var tokenPayloads = resolvedToken .getPayload ();
355361
356- var audience = tokenPayloads .getAudience ();
362+ var audience = tokenPayloads .getAudience (). toArray ( new String []{})[ 0 ] ;
357363 var subject = tokenPayloads .getSubject ();
358364
359365 PredefinedKeys .KEYS .forEach (tokenPayloads ::remove );
@@ -372,8 +378,8 @@ public String renew(String oldToken, Duration expireAfter) {
372378 */
373379 @ Override
374380 public String renew (String oldToken , Duration expireAfter , Map <String , Object > payload ) {
375- var resolvedTokenClaims = resolve (oldToken ).getBody ();
376- var audience = resolvedTokenClaims .getAudience ();
381+ var resolvedTokenClaims = resolve (oldToken ).getPayload ();
382+ var audience = resolvedTokenClaims .getAudience (). toArray ( new String []{})[ 0 ] ;
377383 var subject = resolvedTokenClaims .getSubject ();
378384
379385 return createToken (expireAfter , audience , subject , payload );
@@ -404,8 +410,8 @@ public String renew(String oldToken, Map<String, Object> payload) {
404410 */
405411 @ Override
406412 public <T extends TokenPayload > String renew (String oldToken , Duration expireAfter , T payload ) {
407- var resolvedTokenClaims = resolve (oldToken ).getBody ();
408- var audience = resolvedTokenClaims .getAudience ();
413+ var resolvedTokenClaims = resolve (oldToken ).getPayload ();
414+ var audience = resolvedTokenClaims .getAudience (). toArray ( new String []{})[ 0 ] ;
409415 var subject = resolvedTokenClaims .getSubject ();
410416
411417 return createToken (expireAfter , audience , subject , payload );
@@ -424,4 +430,26 @@ public <T extends TokenPayload> String renew(String oldToken, Duration expireAft
424430 public <T extends TokenPayload > String renew (String oldToken , T payload ) {
425431 return renew (oldToken , Duration .ofMinutes (30 ), payload );
426432 }
433+
434+ private String buildToken (Duration expireAfter , String audience , String subject , Map <String , Object > claims ) {
435+ var now = LocalDateTime .now ();
436+ var builder = Jwts .builder ()
437+ .header ().add ("typ" , "JWT" )
438+ .and ()
439+ .issuedAt (Date .from (now .atZone (ZoneId .systemDefault ()).toInstant ()))
440+ .notBefore (Date .from (now .atZone (ZoneId .systemDefault ()).toInstant ()))
441+ .expiration (Date .from (now .plus (expireAfter ).atZone (ZoneId .systemDefault ()).toInstant ()))
442+ .subject (subject )
443+ .issuer (this .issuer )
444+ .audience ().add (audience )
445+ .and ()
446+ .id (jtiCreator .nextId ().toString ());
447+
448+ if (claims != null && !claims .isEmpty ()) {
449+ builder .claims (claims );
450+ }
451+
452+ return builder .signWith (key , algorithm )
453+ .compact ();
454+ }
427455}
0 commit comments