feat(simple-jwt): Added the feature to handle enumerated data using the base data type.
This commit is contained in:
+27
-5
@@ -23,6 +23,7 @@ import cn.org.codecrafters.simplejwt.SecretCreator;
|
|||||||
import cn.org.codecrafters.simplejwt.TokenPayload;
|
import cn.org.codecrafters.simplejwt.TokenPayload;
|
||||||
import cn.org.codecrafters.simplejwt.TokenResolver;
|
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.annotations.TokenEnum;
|
||||||
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.PredefinedKeys;
|
||||||
@@ -41,6 +42,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
@@ -375,16 +377,28 @@ public class AuthzeroTokenResolver implements TokenResolver<DecodedJWT> {
|
|||||||
var fields = payloadClass.getDeclaredFields();
|
var fields = payloadClass.getDeclaredFields();
|
||||||
|
|
||||||
for (var field : fields) {
|
for (var field : fields) {
|
||||||
|
try {
|
||||||
|
var fieldName = field.getName();
|
||||||
// Skip the fields which are annotated with ExcludeFromPayload
|
// Skip the fields which are annotated with ExcludeFromPayload
|
||||||
if (field.isAnnotationPresent(ExcludeFromPayload.class))
|
if (field.isAnnotationPresent(ExcludeFromPayload.class))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
try {
|
Object invokeObj = payload;
|
||||||
field.setAccessible(true);
|
var getter = payloadClass.getDeclaredMethod("get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1));
|
||||||
|
if (field.isAnnotationPresent(TokenEnum.class)) {
|
||||||
|
var tokenEnum = field.getAnnotation(TokenEnum.class);
|
||||||
|
invokeObj = getter.invoke(payload);
|
||||||
|
getter = field.getType().getDeclaredMethod("get" + tokenEnum.propertyName().substring(0, 1).toUpperCase() + tokenEnum.propertyName().substring(1));
|
||||||
|
}
|
||||||
|
|
||||||
// Build Claims
|
// Build Claims
|
||||||
addClaim(builder, field.getName(), field.get(payload));
|
addClaim(builder, fieldName, getter.invoke(invokeObj));
|
||||||
} catch (IllegalAccessException e) {
|
} catch (IllegalAccessException e) {
|
||||||
log.error("Cannot access field {}!", field.getName());
|
log.error("Cannot access field {}!", field.getName());
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
log.error("Unable to find setter according to given field name.", e);
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
log.info("Cannot invoke method.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -424,9 +438,17 @@ public class AuthzeroTokenResolver implements TokenResolver<DecodedJWT> {
|
|||||||
if (PredefinedKeys.KEYS.contains(entry.getKey()) || targetType.getDeclaredField(entry.getKey()).isAnnotationPresent(ExcludeFromPayload.class))
|
if (PredefinedKeys.KEYS.contains(entry.getKey()) || targetType.getDeclaredField(entry.getKey()).isAnnotationPresent(ExcludeFromPayload.class))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var setter = targetType.getDeclaredMethod("set" + entry.getKey().substring(0, 1).toUpperCase() + entry.getKey().substring(1), entry.getValue().getClass());
|
var field = targetType.getDeclaredField(entry.getKey());
|
||||||
|
var setter = targetType.getDeclaredMethod("set" + entry.getKey().substring(0, 1).toUpperCase() + entry.getKey().substring(1), field.getType());
|
||||||
|
var fieldValue = entry.getValue();
|
||||||
|
if (field.isAnnotationPresent(TokenEnum.class)) {
|
||||||
|
var annotation = field.getAnnotation(TokenEnum.class);
|
||||||
|
var enumStaticLoader = field.getType().getDeclaredMethod("loadBy" + annotation.propertyName().substring(0, 1).toUpperCase() + annotation.propertyName().substring(1), annotation.dataType().getMappedClass());
|
||||||
|
fieldValue = enumStaticLoader.invoke(null, fieldValue);
|
||||||
|
}
|
||||||
|
|
||||||
if (setter.canAccess(bean)) {
|
if (setter.canAccess(bean)) {
|
||||||
setter.invoke(bean, entry.getValue());
|
setter.invoke(bean, fieldValue);
|
||||||
} else {
|
} else {
|
||||||
log.error("Setter for field {} can't be accessed.", entry.getKey());
|
log.error("Setter for field {} can't be accessed.", entry.getKey());
|
||||||
}
|
}
|
||||||
|
|||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 CodeCraftersCN.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
*
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cn.org.codecrafters.simplejwt.annotations;
|
||||||
|
|
||||||
|
import cn.org.codecrafters.simplejwt.constants.TokenDataType;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JwtEnum
|
||||||
|
*
|
||||||
|
* @author Zihlu Wang
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target({ElementType.FIELD})
|
||||||
|
public @interface TokenEnum {
|
||||||
|
|
||||||
|
String propertyName();
|
||||||
|
|
||||||
|
TokenDataType dataType();
|
||||||
|
|
||||||
|
}
|
||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 CodeCraftersCN.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
*
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cn.org.codecrafters.simplejwt.constants;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TokenDataType
|
||||||
|
*
|
||||||
|
* @author Zihlu Wang
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public enum TokenDataType {
|
||||||
|
|
||||||
|
BOOLEAN(Boolean.class),
|
||||||
|
DOUBLE(Long.class),
|
||||||
|
FLOAT(Float.class),
|
||||||
|
INTEGER(Integer.class),
|
||||||
|
LONG(Long.class),
|
||||||
|
STRING(String.class),
|
||||||
|
;
|
||||||
|
|
||||||
|
private final Class<?> mappedClass;
|
||||||
|
|
||||||
|
TokenDataType(Class<?> mappedClass) {
|
||||||
|
this.mappedClass = mappedClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+28
-7
@@ -23,6 +23,7 @@ import cn.org.codecrafters.simplejwt.SecretCreator;
|
|||||||
import cn.org.codecrafters.simplejwt.TokenPayload;
|
import cn.org.codecrafters.simplejwt.TokenPayload;
|
||||||
import cn.org.codecrafters.simplejwt.TokenResolver;
|
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.annotations.TokenEnum;
|
||||||
import cn.org.codecrafters.simplejwt.constants.PredefinedKeys;
|
import cn.org.codecrafters.simplejwt.constants.PredefinedKeys;
|
||||||
import cn.org.codecrafters.simplejwt.constants.TokenAlgorithm;
|
import cn.org.codecrafters.simplejwt.constants.TokenAlgorithm;
|
||||||
import cn.org.codecrafters.simplejwt.exceptions.WeakSecretException;
|
import cn.org.codecrafters.simplejwt.exceptions.WeakSecretException;
|
||||||
@@ -247,14 +248,24 @@ public class JjwtTokenResolver implements TokenResolver<Jws<Claims>> {
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
field.setAccessible(true);
|
var getter = payload.getClass().getDeclaredMethod("get" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1));
|
||||||
// Build Claims
|
// Build Claims
|
||||||
/*
|
/*
|
||||||
* Note (17 Oct, 2023): The jjwt can only add a map to be added.
|
* Note (17 Oct, 2023): The jjwt can only add a map to be added.
|
||||||
*/
|
*/
|
||||||
payloadMap.put(field.getName(), field.get(payload));
|
var fieldValue = getter.invoke(payload);
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
|
// Handle enum fields.
|
||||||
|
if (field.isAnnotationPresent(TokenEnum.class)) {
|
||||||
|
var annotation = field.getAnnotation(TokenEnum.class);
|
||||||
|
var enumGetter = field.getType().getDeclaredMethod("get" + annotation.propertyName().substring(0, 1).toUpperCase() + annotation.propertyName().substring(1));
|
||||||
|
fieldValue = enumGetter.invoke(fieldValue);
|
||||||
|
}
|
||||||
|
payloadMap.put(field.getName(), fieldValue);
|
||||||
|
} catch (IllegalAccessException | NoSuchMethodException e) {
|
||||||
log.error("Cannot access field {}!", field.getName());
|
log.error("Cannot access field {}!", field.getName());
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
log.error("Cannot invoke getter.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,17 +309,27 @@ public class JjwtTokenResolver implements TokenResolver<Jws<Claims>> {
|
|||||||
if (PredefinedKeys.KEYS.contains(entry.getKey()) || targetType.getDeclaredField(entry.getKey()).isAnnotationPresent(ExcludeFromPayload.class))
|
if (PredefinedKeys.KEYS.contains(entry.getKey()) || targetType.getDeclaredField(entry.getKey()).isAnnotationPresent(ExcludeFromPayload.class))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var setter = targetType.getDeclaredMethod("set" + entry.getKey().substring(0, 1).toUpperCase() + entry.getKey().substring(1), entry.getValue().getClass());
|
var field = targetType.getDeclaredField(entry.getKey());
|
||||||
|
var fieldValue = entry.getValue();
|
||||||
|
if (field.isAnnotationPresent(TokenEnum.class)) {
|
||||||
|
var annotation = field.getAnnotation(TokenEnum.class);
|
||||||
|
var enumStaticLoader = field.getType().getDeclaredMethod("loadBy" + annotation.propertyName().substring(0, 1).toUpperCase() + annotation.propertyName().substring(1), annotation.dataType().getMappedClass());
|
||||||
|
fieldValue = enumStaticLoader.invoke(null, entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
var setter = targetType.getDeclaredMethod("set" + entry.getKey().substring(0, 1).toUpperCase() + entry.getKey().substring(1), fieldValue.getClass());
|
||||||
if (setter.canAccess(bean)) {
|
if (setter.canAccess(bean)) {
|
||||||
setter.invoke(bean, entry.getValue());
|
setter.invoke(bean, fieldValue);
|
||||||
} else {
|
} else {
|
||||||
log.error("Setter for field {} can't be accessed.", entry.getKey());
|
log.error("Setter for field {} can't be accessed.", entry.getKey());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return bean;
|
||||||
} catch (InvocationTargetException e) {
|
} catch (InvocationTargetException e) {
|
||||||
log.error("An error occurs while invoking the constructor of type {}.", targetType.getCanonicalName());
|
log.error("Target is not invokable.", e);
|
||||||
} catch (NoSuchMethodException e) {
|
} catch (NoSuchMethodException e) {
|
||||||
log.error("The constructor of the required type {} is not found.", targetType.getCanonicalName());
|
log.error("Cannot find method according to given data.", e);
|
||||||
} catch (InstantiationException e) {
|
} catch (InstantiationException e) {
|
||||||
log.error("The required type {} is abstract or an interface.", targetType.getCanonicalName());
|
log.error("The required type {} is abstract or an interface.", targetType.getCanonicalName());
|
||||||
} catch (IllegalAccessException e) {
|
} catch (IllegalAccessException e) {
|
||||||
|
|||||||
Reference in New Issue
Block a user