From 0951f974289e639e0da28c055a34a3afd49b597b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johnny=20Miller=20=28=E9=94=BA=E4=BF=8A=29?= Date: Sun, 6 Jun 2021 17:19:41 +0800 Subject: [PATCH] feat($Validation): support enum value validation --- .../user/controller/UserController.java | 7 ++ .../user/entity/GetUserStatusPayload.java | 19 +++++ .../authcenter/user/service/UserService.java | 10 +++ .../user/service/impl/UserServiceImpl.java | 11 ++- auto-run-mac.sh | 6 +- .../authcenter/security/UserPrincipal.java | 2 +- .../domain/authcenter/user/UserStatus.java | 16 ++-- pom.xml | 3 +- .../annotation/ValidEnumValue.java | 36 +++++++++ .../validation/EnumValueValidator.java | 77 +++++++++++++++++++ 10 files changed, 171 insertions(+), 16 deletions(-) create mode 100644 auth-center/src/main/java/com/jmsoftware/maf/authcenter/user/entity/GetUserStatusPayload.java create mode 100644 spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/annotation/ValidEnumValue.java create mode 100644 spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/validation/EnumValueValidator.java diff --git a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/user/controller/UserController.java b/auth-center/src/main/java/com/jmsoftware/maf/authcenter/user/controller/UserController.java index 8d3a774a..36b0a901 100644 --- a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/user/controller/UserController.java +++ b/auth-center/src/main/java/com/jmsoftware/maf/authcenter/user/controller/UserController.java @@ -1,5 +1,6 @@ package com.jmsoftware.maf.authcenter.user.controller; +import com.jmsoftware.maf.authcenter.user.entity.GetUserStatusPayload; import com.jmsoftware.maf.authcenter.user.service.UserService; import com.jmsoftware.maf.common.bean.ResponseBodyBean; import com.jmsoftware.maf.common.domain.authcenter.user.LoginPayload; @@ -11,6 +12,7 @@ import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @@ -50,4 +52,9 @@ public ResponseBodyBean login(@Valid @RequestBody LoginPayload pa public ResponseBodyBean logout(HttpServletRequest request) throws SecurityException { return ResponseBodyBean.ofSuccess(userService.logout(request)); } + + @GetMapping("/users/status") + public ResponseBodyBean getUserStatus(@Valid GetUserStatusPayload payload) { + return ResponseBodyBean.ofSuccess(userService.getUserStatus(payload), "Correct enum value"); + } } diff --git a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/user/entity/GetUserStatusPayload.java b/auth-center/src/main/java/com/jmsoftware/maf/authcenter/user/entity/GetUserStatusPayload.java new file mode 100644 index 00000000..6bc4bfaf --- /dev/null +++ b/auth-center/src/main/java/com/jmsoftware/maf/authcenter/user/entity/GetUserStatusPayload.java @@ -0,0 +1,19 @@ +package com.jmsoftware.maf.authcenter.user.entity; + +import com.jmsoftware.maf.common.domain.authcenter.user.UserStatus; +import com.jmsoftware.maf.springcloudstarter.annotation.ValidEnumValue; +import lombok.Data; + +/** + *

GetUserStatusPayload

+ *

+ * Change description here. + * + * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com + * @date 6/6/21 4:31 PM + **/ +@Data +public class GetUserStatusPayload { + @ValidEnumValue(targetEnum = UserStatus.class) + private Byte status; +} diff --git a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/user/service/UserService.java b/auth-center/src/main/java/com/jmsoftware/maf/authcenter/user/service/UserService.java index 7eaeda5c..6bc4fd5f 100644 --- a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/user/service/UserService.java +++ b/auth-center/src/main/java/com/jmsoftware/maf/authcenter/user/service/UserService.java @@ -1,6 +1,7 @@ package com.jmsoftware.maf.authcenter.user.service; import com.baomidou.mybatisplus.extension.service.IService; +import com.jmsoftware.maf.authcenter.user.entity.GetUserStatusPayload; import com.jmsoftware.maf.authcenter.user.entity.UserPersistence; import com.jmsoftware.maf.common.domain.authcenter.user.*; import com.jmsoftware.maf.common.exception.SecurityException; @@ -9,6 +10,7 @@ import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; /** *

UserService

@@ -53,4 +55,12 @@ public interface UserService extends IService { * @throws SecurityException the security exception */ boolean logout(HttpServletRequest request) throws SecurityException; + + /** + * Gets user status. + * + * @param payload the payload + * @return the user status + */ + String getUserStatus(@Valid @NotNull GetUserStatusPayload payload); } diff --git a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/user/service/impl/UserServiceImpl.java b/auth-center/src/main/java/com/jmsoftware/maf/authcenter/user/service/impl/UserServiceImpl.java index 0208df0d..30d5f021 100644 --- a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/user/service/impl/UserServiceImpl.java +++ b/auth-center/src/main/java/com/jmsoftware/maf/authcenter/user/service/impl/UserServiceImpl.java @@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.jmsoftware.maf.authcenter.security.service.JwtService; +import com.jmsoftware.maf.authcenter.user.entity.GetUserStatusPayload; import com.jmsoftware.maf.authcenter.user.entity.UserPersistence; import com.jmsoftware.maf.authcenter.user.mapper.UserMapper; import com.jmsoftware.maf.authcenter.user.service.UserService; @@ -15,7 +16,6 @@ import lombok.extern.slf4j.Slf4j; import lombok.val; import org.springframework.cache.annotation.CacheConfig; -import org.springframework.cache.annotation.Cacheable; import org.springframework.context.MessageSource; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.http.HttpStatus; @@ -25,6 +25,7 @@ import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; /** *

UserServiceImpl

@@ -44,7 +45,6 @@ public class UserServiceImpl extends ServiceImpl im private final MessageSource messageSource; @Override - @Cacheable public GetUserByLoginTokenResponse getUserByLoginToken(@NotBlank String loginToken) { LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); wrapper.and(queryWrapper -> queryWrapper.eq(UserPersistence::getUsername, loginToken) @@ -67,7 +67,7 @@ public SignupResponse saveUserForSignup(@Valid SignupPayload payload) { userPersistence.setUsername(payload.getUsername()); userPersistence.setEmail(payload.getEmail()); userPersistence.setPassword(bCryptPasswordEncoder.encode(payload.getPassword())); - userPersistence.setStatus(UserStatus.ENABLED.getStatus()); + userPersistence.setStatus(UserStatus.ENABLED.getValue()); this.save(userPersistence); log.warn("Saved user for signup. {}", userPersistence); val response = new SignupResponse(); @@ -98,4 +98,9 @@ public boolean logout(HttpServletRequest request) throws SecurityException { jwtService.invalidateJwt(request); return true; } + + @Override + public String getUserStatus(@Valid @NotNull GetUserStatusPayload payload) { + return UserStatus.ofValue(payload.getStatus()).getDescription(); + } } diff --git a/auto-run-mac.sh b/auto-run-mac.sh index 34de3eac..72203683 100755 --- a/auto-run-mac.sh +++ b/auto-run-mac.sh @@ -18,10 +18,10 @@ set -e readonly mavenActiveProfile="development-local" readonly javaParameter="-Xms256m -Xmx256m -Dfile.encoding=UTF-8 -Dspring.cloud.consul.host=localhost -Dspring.profiles.active=$mavenActiveProfile" readonly runServices=( - spring-boot-admin -# api-portal + auth-center + api-gateway ) -readonly skipGitPull=false +readonly skipGitPull=true readonly skipBuild=false # Available options for `startMode`: "keep-previous", "overlap" readonly startMode="overlap" diff --git a/common/src/main/java/com/jmsoftware/maf/common/domain/authcenter/security/UserPrincipal.java b/common/src/main/java/com/jmsoftware/maf/common/domain/authcenter/security/UserPrincipal.java index ca8bddde..48db9960 100644 --- a/common/src/main/java/com/jmsoftware/maf/common/domain/authcenter/security/UserPrincipal.java +++ b/common/src/main/java/com/jmsoftware/maf/common/domain/authcenter/security/UserPrincipal.java @@ -162,6 +162,6 @@ public boolean isCredentialsNonExpired() { @Override public boolean isEnabled() { - return Objects.equals(this.status, UserStatus.ENABLED.getStatus()); + return Objects.equals(this.status, UserStatus.ENABLED.getValue()); } } diff --git a/common/src/main/java/com/jmsoftware/maf/common/domain/authcenter/user/UserStatus.java b/common/src/main/java/com/jmsoftware/maf/common/domain/authcenter/user/UserStatus.java index 9e7e19b2..3dd27393 100644 --- a/common/src/main/java/com/jmsoftware/maf/common/domain/authcenter/user/UserStatus.java +++ b/common/src/main/java/com/jmsoftware/maf/common/domain/authcenter/user/UserStatus.java @@ -19,25 +19,25 @@ public enum UserStatus { */ DISABLED((byte) 0, "Disabled user"); - private final Byte status; + private final Byte value; private final String description; - UserStatus(Byte status, String description) { - this.status = status; + UserStatus(Byte value, String description) { + this.value = value; this.description = description; } /** - * Get user status enum by status value + * Get user value enum by value value * - * @param status status value - * @return user status enum + * @param value value value + * @return user value enum */ - public static UserStatus getByStatus(Byte status) { + public static UserStatus ofValue(Byte value) { UserStatus result = UserStatus.DISABLED; UserStatus[] userStatuses = UserStatus.values(); for (UserStatus userStatus : userStatuses) { - if (userStatus.status.equals(status)) { + if (userStatus.value.equals(value)) { result = userStatus; } } diff --git a/pom.xml b/pom.xml index d4399f0e..d3e39e98 100644 --- a/pom.xml +++ b/pom.xml @@ -43,7 +43,7 @@ 2.4.0 1.2.5 3.4.2 - 5.6.1 + 5.6.4 30.0-jre 3.0.2 0.11.2 @@ -51,6 +51,7 @@ 2.2.6 5.0.0 6.6 + 8.2.1 diff --git a/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/annotation/ValidEnumValue.java b/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/annotation/ValidEnumValue.java new file mode 100644 index 00000000..c6f60b9e --- /dev/null +++ b/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/annotation/ValidEnumValue.java @@ -0,0 +1,36 @@ +package com.jmsoftware.maf.springcloudstarter.annotation; + +import com.jmsoftware.maf.springcloudstarter.validation.EnumValueValidator; + +import javax.validation.Constraint; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + *

ValidEnumValue

+ *

+ * Change description here. + * + * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com + * @date 5/29/21 12:31 PM + **/ +@Documented +@Retention(RUNTIME) +@Target({FIELD, PARAMETER}) +@Constraint(validatedBy = EnumValueValidator.class) +public @interface ValidEnumValue { + String message() default "Invalid enumeration value"; + + Class targetEnum() default Class.class; + + boolean ignoreNull() default false; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/validation/EnumValueValidator.java b/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/validation/EnumValueValidator.java new file mode 100644 index 00000000..69cfd743 --- /dev/null +++ b/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/validation/EnumValueValidator.java @@ -0,0 +1,77 @@ +package com.jmsoftware.maf.springcloudstarter.validation; + +import cn.hutool.core.util.ObjectUtil; +import com.jmsoftware.maf.springcloudstarter.annotation.ValidEnumValue; +import lombok.extern.slf4j.Slf4j; +import lombok.val; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Objects; + +/** + *

EnumValueValidator

+ *

+ * Change description here. + * + * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com + * @date 5/29/21 12:34 PM + **/ +@Slf4j +public class EnumValueValidator implements ConstraintValidator { + private final static String METHOD_NAME = "getValue"; + private ValidEnumValue validEnumValue; + + /** + * {@inheritDoc} + */ + @Override + public void initialize(ValidEnumValue constraintAnnotation) { + ConstraintValidator.super.initialize(constraintAnnotation); + validEnumValue = constraintAnnotation; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isValid(Number value, ConstraintValidatorContext context) { + val enumClass = validEnumValue.targetEnum(); + val ignoreNull = validEnumValue.ignoreNull(); + if (!enumClass.isEnum()) { + log.warn("The given target enum class is not enum! {}", enumClass.getName()); + return false; + } + if (ignoreNull && ObjectUtil.isNull(value)) { + return true; + } + val enumConstantArray = enumClass.getEnumConstants(); + Method method; + try { + method = enumClass.getMethod(METHOD_NAME); + } catch (NoSuchMethodException | SecurityException e) { + log.warn("Did not find the method {} in the class {}", METHOD_NAME, enumClass.getName()); + return false; + } + var validResult = false; + try { + for (var obj : enumConstantArray) { + Object valueDeclaredInEnum = method.invoke(obj); + if (!(valueDeclaredInEnum instanceof Number)) { + log.error("The value declared in enum is not an instance of Number!"); + throw new IllegalArgumentException("The value declared in enum is not an instance of Number!"); + } + if (Objects.deepEquals(value, valueDeclaredInEnum)) { + validResult = true; + break; + } + } + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + log.error("Exception occurred when invoking method! Exception message: {}", e.getMessage()); + return false; + } + return validResult; + } +}