feat(simple-jwt-authzero): Added implementation of renew a token with the data within the old one.
This commit is contained in:
+64
-29
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
package cn.org.codecrafters.simplejwt.authzero;
|
package cn.org.codecrafters.simplejwt.authzero;
|
||||||
|
|
||||||
|
import cn.org.codecrafters.devkit.utils.Base64Util;
|
||||||
import cn.org.codecrafters.guid.GuidCreator;
|
import cn.org.codecrafters.guid.GuidCreator;
|
||||||
import cn.org.codecrafters.simplejwt.SecretCreator;
|
import cn.org.codecrafters.simplejwt.SecretCreator;
|
||||||
import cn.org.codecrafters.simplejwt.TokenPayload;
|
import cn.org.codecrafters.simplejwt.TokenPayload;
|
||||||
@@ -24,12 +25,20 @@ import cn.org.codecrafters.simplejwt.TokenResolver;
|
|||||||
import cn.org.codecrafters.simplejwt.annotations.ExcludeFromPayload;
|
import cn.org.codecrafters.simplejwt.annotations.ExcludeFromPayload;
|
||||||
import cn.org.codecrafters.simplejwt.authzero.config.AuthzeroTokenResolverConfig;
|
import cn.org.codecrafters.simplejwt.authzero.config.AuthzeroTokenResolverConfig;
|
||||||
import cn.org.codecrafters.simplejwt.config.TokenResolverConfig;
|
import cn.org.codecrafters.simplejwt.config.TokenResolverConfig;
|
||||||
|
import cn.org.codecrafters.simplejwt.constants.PredefinedKeys;
|
||||||
import cn.org.codecrafters.simplejwt.constants.TokenAlgorithm;
|
import cn.org.codecrafters.simplejwt.constants.TokenAlgorithm;
|
||||||
import com.auth0.jwt.JWT;
|
import com.auth0.jwt.JWT;
|
||||||
import com.auth0.jwt.JWTCreator;
|
import com.auth0.jwt.JWTCreator;
|
||||||
import com.auth0.jwt.algorithms.Algorithm;
|
import com.auth0.jwt.algorithms.Algorithm;
|
||||||
|
import com.auth0.jwt.interfaces.Claim;
|
||||||
import com.auth0.jwt.interfaces.DecodedJWT;
|
import com.auth0.jwt.interfaces.DecodedJWT;
|
||||||
import com.auth0.jwt.interfaces.JWTVerifier;
|
import com.auth0.jwt.interfaces.JWTVerifier;
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
@@ -114,6 +123,11 @@ public class AuthzeroTokenResolver implements TokenResolver<DecodedJWT> {
|
|||||||
*/
|
*/
|
||||||
private final JWTVerifier verifier;
|
private final JWTVerifier verifier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Jackson JSON handler.
|
||||||
|
*/
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
private final AuthzeroTokenResolverConfig config = AuthzeroTokenResolverConfig.getInstance();
|
private final AuthzeroTokenResolverConfig config = AuthzeroTokenResolverConfig.getInstance();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -127,8 +141,9 @@ public class AuthzeroTokenResolver implements TokenResolver<DecodedJWT> {
|
|||||||
* @param issuer the issuer claim value to be included in JWT tokens
|
* @param issuer the issuer claim value to be included in JWT tokens
|
||||||
* @param secret the secret used for HMAC-based algorithms (HS256,
|
* @param secret the secret used for HMAC-based algorithms (HS256,
|
||||||
* HS384, HS512) for token signing and verification
|
* HS384, HS512) for token signing and verification
|
||||||
|
* @param objectMapper JSON handler
|
||||||
*/
|
*/
|
||||||
public AuthzeroTokenResolver(GuidCreator<?> jtiCreator, TokenAlgorithm algorithm, String issuer, String secret) {
|
public AuthzeroTokenResolver(GuidCreator<?> jtiCreator, TokenAlgorithm algorithm, String issuer, String secret, ObjectMapper objectMapper) {
|
||||||
if (secret == null || secret.isBlank()) {
|
if (secret == null || secret.isBlank()) {
|
||||||
throw new IllegalArgumentException("A secret is required to build a JSON Web Token.");
|
throw new IllegalArgumentException("A secret is required to build a JSON Web Token.");
|
||||||
}
|
}
|
||||||
@@ -143,6 +158,21 @@ public class AuthzeroTokenResolver implements TokenResolver<DecodedJWT> {
|
|||||||
.apply(secret);
|
.apply(secret);
|
||||||
this.issuer = issuer;
|
this.issuer = issuer;
|
||||||
this.verifier = JWT.require(this.algorithm).build();
|
this.verifier = JWT.require(this.algorithm).build();
|
||||||
|
this.objectMapper = objectMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of {@link AuthzeroTokenResolver} with the
|
||||||
|
* provided configurations and a simple UUID GuidCreator.
|
||||||
|
*
|
||||||
|
* @param algorithm the algorithm used for signing and verifying JWT tokens
|
||||||
|
* @param issuer the issuer claim value to be included in JWT tokens
|
||||||
|
* @param secret the secret used for HMAC-based algorithms (HS256,
|
||||||
|
* HS384, HS512) for token signing and verification
|
||||||
|
* @param objectMapper Jackson Databind JSON Handler
|
||||||
|
*/
|
||||||
|
public AuthzeroTokenResolver(TokenAlgorithm algorithm, String issuer, String secret, ObjectMapper objectMapper) {
|
||||||
|
this(UUID::randomUUID, algorithm, issuer, secret, objectMapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -155,20 +185,7 @@ public class AuthzeroTokenResolver implements TokenResolver<DecodedJWT> {
|
|||||||
* HS384, HS512) for token signing and verification
|
* HS384, HS512) for token signing and verification
|
||||||
*/
|
*/
|
||||||
public AuthzeroTokenResolver(TokenAlgorithm algorithm, String issuer, String secret) {
|
public AuthzeroTokenResolver(TokenAlgorithm algorithm, String issuer, String secret) {
|
||||||
if (secret == null || secret.isBlank()) {
|
this(UUID::randomUUID, algorithm, issuer, secret, new ObjectMapper());
|
||||||
throw new IllegalArgumentException("A secret is required to build a JSON Web Token.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (secret.length() <= 32) {
|
|
||||||
log.warn("The provided secret which owns {} characters is too weak. Please consider replacing it with a stronger one.", secret.length());
|
|
||||||
}
|
|
||||||
|
|
||||||
this.jtiCreator = UUID::randomUUID;
|
|
||||||
this.algorithm = config
|
|
||||||
.getAlgorithm(algorithm)
|
|
||||||
.apply(secret);
|
|
||||||
this.issuer = issuer;
|
|
||||||
this.verifier = JWT.require(this.algorithm).build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -181,20 +198,7 @@ public class AuthzeroTokenResolver implements TokenResolver<DecodedJWT> {
|
|||||||
* HS384, HS512) for token signing and verification
|
* HS384, HS512) for token signing and verification
|
||||||
*/
|
*/
|
||||||
public AuthzeroTokenResolver(String issuer, String secret) {
|
public AuthzeroTokenResolver(String issuer, String secret) {
|
||||||
if (secret == null || secret.isBlank()) {
|
this(UUID::randomUUID, TokenAlgorithm.HS256, issuer, secret, new ObjectMapper());
|
||||||
throw new IllegalArgumentException("A secret is required to build a JSON Web Token.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (secret.length() <= 32) {
|
|
||||||
log.warn("The provided secret which owns {} characters is too weak. Please consider replacing it with a stronger one.", secret.length());
|
|
||||||
}
|
|
||||||
|
|
||||||
this.jtiCreator = UUID::randomUUID;
|
|
||||||
this.algorithm = config
|
|
||||||
.getAlgorithm(TokenAlgorithm.HS256)
|
|
||||||
.apply(secret);
|
|
||||||
this.issuer = issuer;
|
|
||||||
this.verifier = JWT.require(this.algorithm).build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -213,6 +217,7 @@ public class AuthzeroTokenResolver implements TokenResolver<DecodedJWT> {
|
|||||||
.apply(secret);
|
.apply(secret);
|
||||||
this.issuer = issuer;
|
this.issuer = issuer;
|
||||||
this.verifier = JWT.require(this.algorithm).build();
|
this.verifier = JWT.require(this.algorithm).build();
|
||||||
|
this.objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
log.info("The secret has been set to {}.", secret);
|
log.info("The secret has been set to {}.", secret);
|
||||||
}
|
}
|
||||||
@@ -448,6 +453,31 @@ public class AuthzeroTokenResolver implements TokenResolver<DecodedJWT> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Re-generate a new token with the payload in the old one.
|
||||||
|
*
|
||||||
|
* @param oldToken the old token
|
||||||
|
* @param expireAfter how long the new token can be valid for
|
||||||
|
* @return re-generated token with the payload in the old one or
|
||||||
|
* {@code null} if an {@link JsonProcessingException} occurred.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String renew(String oldToken, Duration expireAfter) {
|
||||||
|
var resolved = resolve(oldToken);
|
||||||
|
|
||||||
|
try {
|
||||||
|
var payload = objectMapper.readValue(Base64Util.decode(resolved.getPayload()), ObjectNode.class);
|
||||||
|
payload.remove(PredefinedKeys.KEYS);
|
||||||
|
|
||||||
|
var payloadMap = objectMapper.convertValue(payload, new MapTypeReference());
|
||||||
|
return createToken(expireAfter, resolved.getAudience().get(0), resolved.getSubject(), payloadMap);
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
log.error("Cannot read payload content, error details:", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renews the given expired token with the specified custom payload data.
|
* Renews the given expired token with the specified custom payload data.
|
||||||
*
|
*
|
||||||
@@ -509,4 +539,9 @@ public class AuthzeroTokenResolver implements TokenResolver<DecodedJWT> {
|
|||||||
public <T extends TokenPayload> String renew(String oldToken, T payload) {
|
public <T extends TokenPayload> String renew(String oldToken, T payload) {
|
||||||
return renew(oldToken, Duration.ofMinutes(30), payload);
|
return renew(oldToken, Duration.ofMinutes(30), payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class MapTypeReference extends TypeReference<Map<String, Object>> {
|
||||||
|
MapTypeReference() {
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-3
@@ -22,6 +22,7 @@ import cn.org.codecrafters.simplejwt.TokenResolver;
|
|||||||
import cn.org.codecrafters.simplejwt.authzero.AuthzeroTokenResolver;
|
import cn.org.codecrafters.simplejwt.authzero.AuthzeroTokenResolver;
|
||||||
import cn.org.codecrafters.simplejwt.autoconfiguration.properties.SimpleJwtProperties;
|
import cn.org.codecrafters.simplejwt.autoconfiguration.properties.SimpleJwtProperties;
|
||||||
import com.auth0.jwt.interfaces.DecodedJWT;
|
import com.auth0.jwt.interfaces.DecodedJWT;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||||
@@ -31,7 +32,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
|||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.DependsOn;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@code AuthzeroTokenResolverAutoConfiguration} is responsible for
|
* {@code AuthzeroTokenResolverAutoConfiguration} is responsible for
|
||||||
@@ -75,16 +75,20 @@ public class AuthzeroTokenResolverAutoConfiguration {
|
|||||||
*/
|
*/
|
||||||
private final SimpleJwtProperties simpleJwtProperties;
|
private final SimpleJwtProperties simpleJwtProperties;
|
||||||
|
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new {@code SimpleJwtAutoConfiguration} instance with the
|
* Constructs a new {@code SimpleJwtAutoConfiguration} instance with the
|
||||||
* provided SimpleJwtProperties.
|
* provided SimpleJwtProperties.
|
||||||
*
|
*
|
||||||
* @param simpleJwtProperties the SimpleJwtProperties instance
|
* @param simpleJwtProperties the SimpleJwtProperties instance
|
||||||
|
* @param objectMapper Jackson JSON Handler
|
||||||
*/
|
*/
|
||||||
@Autowired
|
@Autowired
|
||||||
public AuthzeroTokenResolverAutoConfiguration(SimpleJwtProperties simpleJwtProperties, GuidCreator<?> jtiCreator) {
|
public AuthzeroTokenResolverAutoConfiguration(SimpleJwtProperties simpleJwtProperties, GuidCreator<?> jtiCreator, ObjectMapper objectMapper) {
|
||||||
this.jtiCreator = jtiCreator;
|
this.jtiCreator = jtiCreator;
|
||||||
this.simpleJwtProperties = simpleJwtProperties;
|
this.simpleJwtProperties = simpleJwtProperties;
|
||||||
|
this.objectMapper = objectMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -102,7 +106,8 @@ public class AuthzeroTokenResolverAutoConfiguration {
|
|||||||
jtiCreator,
|
jtiCreator,
|
||||||
simpleJwtProperties.algorithm(),
|
simpleJwtProperties.algorithm(),
|
||||||
simpleJwtProperties.issuer(),
|
simpleJwtProperties.issuer(),
|
||||||
simpleJwtProperties.secret()
|
simpleJwtProperties.secret(),
|
||||||
|
objectMapper
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user