Skip to content

Commit c0aa871

Browse files
committed
feat(simple-jwt): Added the feature to handle enumerated data using the base data type.
1 parent 770bcc3 commit c0aa871

File tree

4 files changed

+142
-15
lines changed

4 files changed

+142
-15
lines changed

simple-jwt-authzero/src/main/java/cn/org/codecrafters/simplejwt/authzero/AuthzeroTokenResolver.java

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import cn.org.codecrafters.simplejwt.TokenPayload;
2424
import cn.org.codecrafters.simplejwt.TokenResolver;
2525
import cn.org.codecrafters.simplejwt.annotations.ExcludeFromPayload;
26+
import cn.org.codecrafters.simplejwt.annotations.TokenEnum;
2627
import cn.org.codecrafters.simplejwt.authzero.config.AuthzeroTokenResolverConfig;
2728
import cn.org.codecrafters.simplejwt.config.TokenResolverConfig;
2829
import cn.org.codecrafters.simplejwt.constants.PredefinedKeys;
@@ -41,6 +42,7 @@
4142
import com.fasterxml.jackson.databind.node.ObjectNode;
4243
import lombok.extern.slf4j.Slf4j;
4344

45+
import java.lang.reflect.Field;
4446
import java.lang.reflect.InvocationTargetException;
4547
import java.time.Duration;
4648
import java.time.LocalDateTime;
@@ -375,16 +377,28 @@ public <T extends TokenPayload> String createToken(Duration expireAfter, String
375377
var fields = payloadClass.getDeclaredFields();
376378

377379
for (var field : fields) {
378-
// Skip the fields which are annotated with ExcludeFromPayload
379-
if (field.isAnnotationPresent(ExcludeFromPayload.class))
380-
continue;
381-
382380
try {
383-
field.setAccessible(true);
381+
var fieldName = field.getName();
382+
// Skip the fields which are annotated with ExcludeFromPayload
383+
if (field.isAnnotationPresent(ExcludeFromPayload.class))
384+
continue;
385+
386+
Object invokeObj = payload;
387+
var getter = payloadClass.getDeclaredMethod("get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1));
388+
if (field.isAnnotationPresent(TokenEnum.class)) {
389+
var tokenEnum = field.getAnnotation(TokenEnum.class);
390+
invokeObj = getter.invoke(payload);
391+
getter = field.getType().getDeclaredMethod("get" + tokenEnum.propertyName().substring(0, 1).toUpperCase() + tokenEnum.propertyName().substring(1));
392+
}
393+
384394
// Build Claims
385-
addClaim(builder, field.getName(), field.get(payload));
395+
addClaim(builder, fieldName, getter.invoke(invokeObj));
386396
} catch (IllegalAccessException e) {
387397
log.error("Cannot access field {}!", field.getName());
398+
} catch (NoSuchMethodException e) {
399+
log.error("Unable to find setter according to given field name.", e);
400+
} catch (InvocationTargetException e) {
401+
log.info("Cannot invoke method.", e);
388402
}
389403
}
390404

@@ -424,9 +438,17 @@ public <T extends TokenPayload> T extract(String token, Class<T> targetType) {
424438
if (PredefinedKeys.KEYS.contains(entry.getKey()) || targetType.getDeclaredField(entry.getKey()).isAnnotationPresent(ExcludeFromPayload.class))
425439
continue;
426440

427-
var setter = targetType.getDeclaredMethod("set" + entry.getKey().substring(0, 1).toUpperCase() + entry.getKey().substring(1), entry.getValue().getClass());
441+
var field = targetType.getDeclaredField(entry.getKey());
442+
var setter = targetType.getDeclaredMethod("set" + entry.getKey().substring(0, 1).toUpperCase() + entry.getKey().substring(1), field.getType());
443+
var fieldValue = entry.getValue();
444+
if (field.isAnnotationPresent(TokenEnum.class)) {
445+
var annotation = field.getAnnotation(TokenEnum.class);
446+
var enumStaticLoader = field.getType().getDeclaredMethod("loadBy" + annotation.propertyName().substring(0, 1).toUpperCase() + annotation.propertyName().substring(1), annotation.dataType().getMappedClass());
447+
fieldValue = enumStaticLoader.invoke(null, fieldValue);
448+
}
449+
428450
if (setter.canAccess(bean)) {
429-
setter.invoke(bean, entry.getValue());
451+
setter.invoke(bean, fieldValue);
430452
} else {
431453
log.error("Setter for field {} can't be accessed.", entry.getKey());
432454
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright (C) 2023 CodeCraftersCN.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
*
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package cn.org.codecrafters.simplejwt.annotations;
19+
20+
import cn.org.codecrafters.simplejwt.constants.TokenDataType;
21+
22+
import java.lang.annotation.ElementType;
23+
import java.lang.annotation.Retention;
24+
import java.lang.annotation.RetentionPolicy;
25+
import java.lang.annotation.Target;
26+
27+
/**
28+
* JwtEnum
29+
*
30+
* @author Zihlu Wang
31+
*/
32+
@Retention(RetentionPolicy.RUNTIME)
33+
@Target({ElementType.FIELD})
34+
public @interface TokenEnum {
35+
36+
String propertyName();
37+
38+
TokenDataType dataType();
39+
40+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright (C) 2023 CodeCraftersCN.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
*
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package cn.org.codecrafters.simplejwt.constants;
19+
20+
import lombok.Getter;
21+
22+
/**
23+
* TokenDataType
24+
*
25+
* @author Zihlu Wang
26+
*/
27+
@Getter
28+
public enum TokenDataType {
29+
30+
BOOLEAN(Boolean.class),
31+
DOUBLE(Long.class),
32+
FLOAT(Float.class),
33+
INTEGER(Integer.class),
34+
LONG(Long.class),
35+
STRING(String.class),
36+
;
37+
38+
private final Class<?> mappedClass;
39+
40+
TokenDataType(Class<?> mappedClass) {
41+
this.mappedClass = mappedClass;
42+
}
43+
44+
}

simple-jwt-jjwt/src/main/java/cn/org/codecrafters/simplejwt/jjwt/JjwtTokenResolver.java

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import cn.org.codecrafters.simplejwt.TokenPayload;
2424
import cn.org.codecrafters.simplejwt.TokenResolver;
2525
import cn.org.codecrafters.simplejwt.annotations.ExcludeFromPayload;
26+
import cn.org.codecrafters.simplejwt.annotations.TokenEnum;
2627
import cn.org.codecrafters.simplejwt.constants.PredefinedKeys;
2728
import cn.org.codecrafters.simplejwt.constants.TokenAlgorithm;
2829
import cn.org.codecrafters.simplejwt.exceptions.WeakSecretException;
@@ -247,14 +248,24 @@ public <T extends TokenPayload> String createToken(Duration expireAfter, String
247248
continue;
248249

249250
try {
250-
field.setAccessible(true);
251+
var getter = payload.getClass().getDeclaredMethod("get" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1));
251252
// Build Claims
252253
/*
253254
* Note (17 Oct, 2023): The jjwt can only add a map to be added.
254255
*/
255-
payloadMap.put(field.getName(), field.get(payload));
256-
} catch (IllegalAccessException e) {
256+
var fieldValue = getter.invoke(payload);
257+
258+
// Handle enum fields.
259+
if (field.isAnnotationPresent(TokenEnum.class)) {
260+
var annotation = field.getAnnotation(TokenEnum.class);
261+
var enumGetter = field.getType().getDeclaredMethod("get" + annotation.propertyName().substring(0, 1).toUpperCase() + annotation.propertyName().substring(1));
262+
fieldValue = enumGetter.invoke(fieldValue);
263+
}
264+
payloadMap.put(field.getName(), fieldValue);
265+
} catch (IllegalAccessException | NoSuchMethodException e) {
257266
log.error("Cannot access field {}!", field.getName());
267+
} catch (InvocationTargetException e) {
268+
log.error("Cannot invoke getter.", e);
258269
}
259270
}
260271

@@ -298,17 +309,27 @@ public <T extends TokenPayload> T extract(String token, Class<T> targetType) {
298309
if (PredefinedKeys.KEYS.contains(entry.getKey()) || targetType.getDeclaredField(entry.getKey()).isAnnotationPresent(ExcludeFromPayload.class))
299310
continue;
300311

301-
var setter = targetType.getDeclaredMethod("set" + entry.getKey().substring(0, 1).toUpperCase() + entry.getKey().substring(1), entry.getValue().getClass());
312+
var field = targetType.getDeclaredField(entry.getKey());
313+
var fieldValue = entry.getValue();
314+
if (field.isAnnotationPresent(TokenEnum.class)) {
315+
var annotation = field.getAnnotation(TokenEnum.class);
316+
var enumStaticLoader = field.getType().getDeclaredMethod("loadBy" + annotation.propertyName().substring(0, 1).toUpperCase() + annotation.propertyName().substring(1), annotation.dataType().getMappedClass());
317+
fieldValue = enumStaticLoader.invoke(null, entry.getValue());
318+
}
319+
320+
var setter = targetType.getDeclaredMethod("set" + entry.getKey().substring(0, 1).toUpperCase() + entry.getKey().substring(1), fieldValue.getClass());
302321
if (setter.canAccess(bean)) {
303-
setter.invoke(bean, entry.getValue());
322+
setter.invoke(bean, fieldValue);
304323
} else {
305324
log.error("Setter for field {} can't be accessed.", entry.getKey());
306325
}
307326
}
327+
328+
return bean;
308329
} catch (InvocationTargetException e) {
309-
log.error("An error occurs while invoking the constructor of type {}.", targetType.getCanonicalName());
330+
log.error("Target is not invokable.", e);
310331
} catch (NoSuchMethodException e) {
311-
log.error("The constructor of the required type {} is not found.", targetType.getCanonicalName());
332+
log.error("Cannot find method according to given data.", e);
312333
} catch (InstantiationException e) {
313334
log.error("The required type {} is abstract or an interface.", targetType.getCanonicalName());
314335
} catch (IllegalAccessException e) {

0 commit comments

Comments
 (0)