Jelajahi Sumber

优化参数校验异常处理逻辑

Woody 1 Minggu lalu
induk
melakukan
f88817580d
16 mengubah file dengan 125 tambahan dan 104 penghapusan
  1. 1 2
      framework-base/src/main/java/com/chelvc/framework/base/annotation/Version.java
  2. 12 12
      framework-base/src/main/java/com/chelvc/framework/base/context/DefaultSessionFactory.java
  3. 5 26
      framework-base/src/main/java/com/chelvc/framework/base/context/Session.java
  4. 25 32
      framework-base/src/main/java/com/chelvc/framework/base/context/SessionContextHolder.java
  5. 8 7
      framework-base/src/main/java/com/chelvc/framework/base/interceptor/GlobalExceptionInterceptor.java
  6. 1 12
      framework-base/src/main/java/com/chelvc/framework/base/interceptor/VersionControlInterceptor.java
  7. 1 1
      framework-common/src/main/java/com/chelvc/framework/common/annotation/Dates.java
  8. 1 1
      framework-common/src/main/java/com/chelvc/framework/common/annotation/Enumerations.java
  9. 1 1
      framework-common/src/main/java/com/chelvc/framework/common/annotation/Numbers.java
  10. 1 1
      framework-common/src/main/java/com/chelvc/framework/common/annotation/Pages.java
  11. 1 1
      framework-common/src/main/java/com/chelvc/framework/common/annotation/Strings.java
  12. 31 0
      framework-security/src/main/java/com/chelvc/framework/security/context/SecurityContextHolder.java
  13. 6 3
      framework-security/src/main/java/com/chelvc/framework/security/interceptor/SecurityValidateInterceptor.java
  14. 24 1
      framework-security/src/main/java/com/chelvc/framework/security/session/DefaultSessionValidator.java
  15. 2 2
      framework-security/src/main/java/com/chelvc/framework/security/session/OAuthSessionFactory.java
  16. 5 2
      framework-security/src/main/java/com/chelvc/framework/security/session/RedisSessionValidator.java

+ 1 - 2
framework-base/src/main/java/com/chelvc/framework/base/annotation/Version.java

@@ -7,7 +7,6 @@ import java.lang.annotation.Target;
 
 import com.chelvc.framework.common.model.Compare;
 import com.chelvc.framework.common.model.Terminal;
-import com.chelvc.framework.common.util.StringUtils;
 
 /**
  * 客户端版本号注解
@@ -45,5 +44,5 @@ public @interface Version {
      *
      * @return 异常消息
      */
-    String message() default StringUtils.EMPTY;
+    String message() default "Version.Limit";
 }

+ 12 - 12
framework-base/src/main/java/com/chelvc/framework/base/context/DefaultSessionFactory.java

@@ -114,6 +114,16 @@ public class DefaultSessionFactory implements SessionFactory {
         return StringUtils.ifEmpty(request.getHeader(SessionContextHolder.HEADER_CHANNEL), (String) null);
     }
 
+    /**
+     * 获取客户端版本号
+     *
+     * @param request Http请求对象
+     * @return 版本号
+     */
+    protected String getVersion(@NonNull HttpServletRequest request) {
+        return StringUtils.ifEmpty(request.getHeader(SessionContextHolder.HEADER_VERSION), (String) null);
+    }
+
     /**
      * 获取客户端平台标识
      *
@@ -144,16 +154,6 @@ public class DefaultSessionFactory implements SessionFactory {
         return null;
     }
 
-    /**
-     * 获取客户端版本号
-     *
-     * @param request Http请求对象
-     * @return 版本号
-     */
-    protected String getVersion(@NonNull HttpServletRequest request) {
-        return StringUtils.ifEmpty(request.getHeader(SessionContextHolder.HEADER_VERSION), (String) null);
-    }
-
     /**
      * 获取请求时间戳
      *
@@ -206,8 +206,8 @@ public class DefaultSessionFactory implements SessionFactory {
     public Session build(@NonNull HttpServletRequest request) {
         return Session.builder().id(this.getId(request)).rid(this.getRid(request)).using(this.getUsing(request))
                 .host(this.getHost(request)).scope(this.getScope(request)).mobile(this.getMobile(request))
-                .device(this.getDevice(request)).channel(this.getChannel(request)).platform(this.getPlatform(request))
-                .terminal(this.getTerminal(request)).version(this.getVersion(request))
+                .device(this.getDevice(request)).channel(this.getChannel(request)).version(this.getVersion(request))
+                .platform(this.getPlatform(request)).terminal(this.getTerminal(request))
                 .timestamp(this.getTimestamp(request)).registering(this.getRegistering(request))
                 .authorities(Collections.unmodifiableSet(this.getAuthorities(request))).build();
     }

+ 5 - 26
framework-base/src/main/java/com/chelvc/framework/base/context/Session.java

@@ -1,7 +1,6 @@
 package com.chelvc.framework.base.context;
 
 import java.io.Serializable;
-import java.util.Collections;
 import java.util.Map;
 import java.util.Set;
 
@@ -76,6 +75,11 @@ public class Session implements Serializable {
      */
     private String channel;
 
+    /**
+     * 终端版本
+     */
+    private String version;
+
     /**
      * 应用平台
      */
@@ -86,11 +90,6 @@ public class Session implements Serializable {
      */
     private Terminal terminal;
 
-    /**
-     * 终端版本
-     */
-    private String version;
-
     /**
      * 请求时间戳
      */
@@ -134,24 +133,4 @@ public class Session implements Serializable {
     public void setGroup(@NonNull String scene, @NonNull Caps group) {
         this.groups.put(scene, group);
     }
-
-    /**
-     * 初始化会话主体信息
-     *
-     * @param id          主体标识
-     * @param using       使用类别
-     * @param scope       应用范围
-     * @param mobile      手机号码
-     * @param registering 注册时间戳
-     * @param authorities 权限标识集合
-     */
-    void initializePrincipal(@NonNull Long id, @NonNull Using using, @NonNull String scope, String mobile,
-                             Long registering, @NonNull Set<String> authorities) {
-        this.id = id;
-        this.using = using;
-        this.scope = scope;
-        this.mobile = mobile;
-        this.registering = registering;
-        this.authorities = Collections.unmodifiableSet(authorities);
-    }
 }

+ 25 - 32
framework-base/src/main/java/com/chelvc/framework/base/context/SessionContextHolder.java

@@ -70,6 +70,11 @@ public class SessionContextHolder implements ServletRequestListener {
      */
     public static final String HEADER_CHANNEL = "channel";
 
+    /**
+     * 终端版本号请求头
+     */
+    public static final String HEADER_VERSION = "version";
+
     /**
      * 应用平台请求头
      */
@@ -80,11 +85,6 @@ public class SessionContextHolder implements ServletRequestListener {
      */
     public static final String HEADER_TERMINAL = "terminal";
 
-    /**
-     * 终端版本号请求头
-     */
-    public static final String HEADER_VERSION = "version";
-
     /**
      * 请求时间戳请求头
      */
@@ -148,53 +148,46 @@ public class SessionContextHolder implements ServletRequestListener {
     }
 
     /**
-     * 设置会话信息,如果当前已存在会话则更新会话主体信息
-     *
-     * @param id    主体标识
-     * @param scope 应用范围
-     * @return 会话信息
-     */
-    public static Session setSession(@NonNull Long id, @NonNull String scope) {
-        return setSession(id, scope, Collections.emptySet());
-    }
-
-    /**
-     * 设置会话信息,如果当前已存在会话则更新会话主体信息
+     * 更新会话信息
      *
      * @param id          主体标识
+     * @param using       使用类别
      * @param scope       应用范围
+     * @param mobile      手机号码
+     * @param registering 注册时间戳
      * @param authorities 权限标识集合
      * @return 会话信息
      */
-    public static Session setSession(@NonNull Long id, @NonNull String scope, @NonNull Set<String> authorities) {
-        return setSession(id, Using.NORMAL, scope, null, null, authorities);
+    public static Session updateSession(@NonNull Long id, @NonNull Using using, @NonNull String scope, String mobile,
+                                        Long registering, @NonNull Set<String> authorities) {
+        return updateSession(id, using, scope, mobile, getVersion(), getPlatform(), registering, authorities);
     }
 
     /**
-     * 设置会话信息,如果当前已存在会话则更新会话主体信息
+     * 更新会话信息
      *
      * @param id          主体标识
      * @param using       使用类别
      * @param scope       应用范围
      * @param mobile      手机号码
+     * @param version     客户端版本
+     * @param platform    客户端平台
      * @param registering 注册时间戳
      * @param authorities 权限标识集合
      * @return 会话信息
      */
-    public static Session setSession(@NonNull Long id, @NonNull Using using, @NonNull String scope, String mobile,
-                                     Long registering, @NonNull Set<String> authorities) {
+    public static Session updateSession(@NonNull Long id, @NonNull Using using, @NonNull String scope, String mobile,
+                                        String version, Platform platform, Long registering,
+                                        @NonNull Set<String> authorities) {
         Deque<Session> deque = SESSION_CONTEXT.get();
-        Session session = deque.peek();
-        if (session == null || session == EMPTY_SESSION) {
-            if (session != null) {
-                deque.poll();
-            }
-            session = Session.builder().id(id).using(using).scope(scope).mobile(mobile).registering(registering)
-                    .authorities(Collections.unmodifiableSet(authorities))
-                    .timestamp(System.currentTimeMillis()).build();
+        Session session = deque.poll();
+        if (session != null) {
+            session = Session.builder().id(id).rid(session.getRid()).using(using).host(session.getHost())
+                    .scope(scope).mobile(mobile).device(session.getDevice()).channel(session.getChannel())
+                    .version(version).platform(platform).terminal(session.getTerminal())
+                    .timestamp(session.getTimestamp()).registering(registering)
+                    .authorities(Collections.unmodifiableSet(authorities)).build();
             deque.push(session);
-        } else {
-            session.initializePrincipal(id, using, scope, mobile, registering, authorities);
         }
         return session;
     }

+ 8 - 7
framework-base/src/main/java/com/chelvc/framework/base/interceptor/GlobalExceptionInterceptor.java

@@ -130,7 +130,7 @@ public class GlobalExceptionInterceptor extends AbstractErrorController implemen
             if (Objects.isNull(e.getDefaultMessage()) || e.contains(TypeMismatchException.class)) {
                 return ApplicationContextHolder.getMessage("Parameter.Invalid");
             }
-            return e.getDefaultMessage();
+            return ApplicationContextHolder.getMessage(e.getDefaultMessage());
         }, (o, n) -> n));
     }
 
@@ -164,12 +164,14 @@ public class GlobalExceptionInterceptor extends AbstractErrorController implemen
             return Collections.emptyMap();
         }
         return errors.stream().filter(Objects::nonNull).collect(Collectors.toMap(
+                error -> String.valueOf(error.getPropertyPath()),
                 error -> {
-                    String path = String.valueOf(error.getPropertyPath());
-                    int index = path.lastIndexOf('.');
-                    return index > -1 ? path.substring(index + 1) : path;
+                    if (Objects.isNull(error.getMessage())) {
+                        return ApplicationContextHolder.getMessage("Parameter.Invalid");
+                    }
+                    return ApplicationContextHolder.getMessage(error.getMessage());
                 },
-                error -> ObjectUtils.ifNull(error.getMessage(), StringUtils.EMPTY)
+                (o, n) -> n
         ));
     }
 
@@ -180,8 +182,7 @@ public class GlobalExceptionInterceptor extends AbstractErrorController implemen
      * @return 参数名称/异常消息映射表
      */
     private Map<String, String> format(MethodArgumentTypeMismatchException e) {
-        return ImmutableMap.of(e.getName(),
-                ObjectUtils.ifNull(e.getRequiredType(), Class::getSimpleName, () -> StringUtils.EMPTY));
+        return ImmutableMap.of(e.getName(), ApplicationContextHolder.getMessage("Parameter.Invalid"));
     }
 
     /**

+ 1 - 12
framework-base/src/main/java/com/chelvc/framework/base/interceptor/VersionControlInterceptor.java

@@ -45,24 +45,13 @@ public class VersionControlInterceptor implements HandlerInterceptor, WebMvcConf
             versions = ObjectUtils.ifNull(method.getBeanType().getAnnotation(Versions.class), Versions::value);
         }
         if (ObjectUtils.notEmpty(versions)) {
-            Environment environment = ApplicationContextHolder.getEnvironment(false);
             for (Version version : versions) {
                 if (version.terminal() == terminal && StringUtils.notEmpty(version.value())
                         && !SessionContextHolder.isVersion(version)) {
-                    String message = version.message();
-                    if (environment != null && StringUtils.notEmpty(message)) {
-                        message = environment.resolvePlaceholders(message);
-                    }
-                    throw new ResourceUnavailableException(message);
+                    throw new ResourceUnavailableException(ApplicationContextHolder.getMessage(version.message()));
                 }
             }
         }
-
-        // 终端最低版本配置校验
-        String minimum = ApplicationContextHolder.getProperty(terminal.name().toLowerCase() + ".version.minimum");
-        if (StringUtils.notEmpty(minimum) && !SessionContextHolder.isAfterVersion(terminal, minimum, true)) {
-            throw new ResourceUnavailableException(ApplicationContextHolder.getMessage("Version.Minimum.Limit"));
-        }
         return true;
     }
 

+ 1 - 1
framework-common/src/main/java/com/chelvc/framework/common/annotation/Dates.java

@@ -40,7 +40,7 @@ public @interface Dates {
      *
      * @return 异常消息
      */
-    String message() default "Invalid date";
+    String message() default "Parameter.Invalid";
 
     /**
      * 获取对象分组(固定写法)

+ 1 - 1
framework-common/src/main/java/com/chelvc/framework/common/annotation/Enumerations.java

@@ -33,7 +33,7 @@ public @interface Enumerations {
      *
      * @return 异常消息
      */
-    String message() default "Invalid enumeration";
+    String message() default "Parameter.Invalid";
 
     /**
      * 获取对象分组(固定写法)

+ 1 - 1
framework-common/src/main/java/com/chelvc/framework/common/annotation/Numbers.java

@@ -40,7 +40,7 @@ public @interface Numbers {
      *
      * @return 异常消息
      */
-    String message() default "Invalid number";
+    String message() default "Parameter.Invalid";
 
     /**
      * 获取对象分组(固定写法)

+ 1 - 1
framework-common/src/main/java/com/chelvc/framework/common/annotation/Pages.java

@@ -40,7 +40,7 @@ public @interface Pages {
      *
      * @return 异常消息
      */
-    String message() default "Invalid paging";
+    String message() default "Parameter.Invalid";
 
     /**
      * 获取对象分组(固定写法)

+ 1 - 1
framework-common/src/main/java/com/chelvc/framework/common/annotation/Strings.java

@@ -48,7 +48,7 @@ public @interface Strings {
      *
      * @return 异常消息
      */
-    String message() default "Invalid string";
+    String message() default "Parameter.Invalid";
 
     /**
      * 获取对象分组(固定写法)

+ 31 - 0
framework-security/src/main/java/com/chelvc/framework/security/context/SecurityContextHolder.java

@@ -8,6 +8,7 @@ import java.util.Set;
 import com.chelvc.framework.base.context.ApplicationContextHolder;
 import com.chelvc.framework.base.util.HttpUtils;
 import com.chelvc.framework.base.util.SpringUtils;
+import com.chelvc.framework.common.model.Platform;
 import com.chelvc.framework.common.util.ObjectUtils;
 import com.chelvc.framework.common.util.StringUtils;
 import com.chelvc.framework.security.annotation.Security;
@@ -44,6 +45,16 @@ public final class SecurityContextHolder {
      */
     public static final String MOBILE = "mobile";
 
+    /**
+     * 客户端版本
+     */
+    public static final String VERSION = "version";
+
+    /**
+     * 客户端平台
+     */
+    public static final String PLATFORM = "platform";
+
     /**
      * 用户账号标识
      */
@@ -107,6 +118,26 @@ public final class SecurityContextHolder {
         return jwt != null && Boolean.TRUE.equals(jwt.getClaimAsBoolean(CLIENT));
     }
 
+    /**
+     * 获取版本号
+     *
+     * @param jwt Jwt对象
+     * @return 版本号
+     */
+    public static String getVersion(Jwt jwt) {
+        return jwt == null ? null : StringUtils.ifEmpty(jwt.getClaimAsString(VERSION), (String) null);
+    }
+
+    /**
+     * 获取平台信息
+     *
+     * @param jwt Jwt对象
+     * @return 平台信息
+     */
+    public static Platform getPlatform(Jwt jwt) {
+        return jwt == null ? null : StringUtils.ifEmpty(jwt.getClaimAsString(PLATFORM), Platform::valueOf);
+    }
+
     /**
      * 获取用户授权信息
      *

+ 6 - 3
framework-security/src/main/java/com/chelvc/framework/security/interceptor/SecurityValidateInterceptor.java

@@ -135,11 +135,14 @@ public class SecurityValidateInterceptor implements HandlerInterceptor, WebMvcCo
         }
 
         if (security == null || security.header()) {
-            // 请求头非空校验
+            // 请求头校验
             Long timestamp = session.getTimestamp();
+            String version = SessionContextHolder.getHeader(SessionContextHolder.HEADER_VERSION);
+            String platform = SessionContextHolder.getHeader(SessionContextHolder.HEADER_PLATFORM);
             if (timestamp == null || session.getPlatform() == null || session.getTerminal() == null
-                    || StringUtils.isEmpty(session.getVersion())) {
-                String message = ApplicationContextHolder.getMessage("Header.Missing");
+                    || StringUtils.isEmpty(session.getVersion()) || !Objects.equals(session.getVersion(), version)
+                    || !Objects.equals(session.getPlatform().name(), platform)) {
+                String message = ApplicationContextHolder.getMessage("Header.Invalid");
                 if (this.isObservable()) {
                     LoggingContextHolder.warn(log, request, message);
                 } else {

+ 24 - 1
framework-security/src/main/java/com/chelvc/framework/security/session/DefaultSessionValidator.java

@@ -4,6 +4,8 @@ import java.util.Set;
 
 import com.chelvc.framework.base.context.ApplicationContextHolder;
 import com.chelvc.framework.base.context.SessionContextHolder;
+import com.chelvc.framework.base.context.Using;
+import com.chelvc.framework.common.model.Platform;
 import com.chelvc.framework.security.context.SecurityContextHolder;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
@@ -46,6 +48,26 @@ public class DefaultSessionValidator implements SessionValidator {
         return SecurityContextHolder.getScope(jwt);
     }
 
+    /**
+     * 获取版本号
+     *
+     * @param jwt Jwt对象
+     * @return 版本号
+     */
+    protected String getVersion(Jwt jwt) {
+        return SecurityContextHolder.getVersion(jwt);
+    }
+
+    /**
+     * 获取平台信息
+     *
+     * @param jwt Jwt对象
+     * @return 平台信息
+     */
+    protected Platform getPlatform(Jwt jwt) {
+        return SecurityContextHolder.getPlatform(jwt);
+    }
+
     /**
      * 获取用户授权信息
      *
@@ -59,7 +81,8 @@ public class DefaultSessionValidator implements SessionValidator {
     @Override
     public OAuth2TokenValidatorResult validate(Jwt jwt) {
         if (!SecurityContextHolder.isClient(jwt)) {
-            SessionContextHolder.setSession(this.getId(jwt), this.getScope(jwt), this.getAuthorities(jwt));
+            SessionContextHolder.updateSession(this.getId(jwt), Using.NORMAL, this.getScope(jwt), null,
+                    this.getVersion(jwt), this.getPlatform(jwt), null, this.getAuthorities(jwt));
         }
         return OAuth2TokenValidatorResult.success();
     }

+ 2 - 2
framework-security/src/main/java/com/chelvc/framework/security/session/OAuthSessionFactory.java

@@ -21,8 +21,8 @@ public class OAuthSessionFactory extends DefaultSessionFactory implements Sessio
     @Override
     public Session build(@NonNull HttpServletRequest request) {
         return Session.builder().rid(this.getRid(request)).using(Using.NORMAL).host(this.getHost(request))
-                .device(this.getDevice(request)).channel(this.getChannel(request)).platform(this.getPlatform(request))
-                .terminal(this.getTerminal(request)).version(this.getVersion(request))
+                .device(this.getDevice(request)).channel(this.getChannel(request)).version(this.getVersion(request))
+                .platform(this.getPlatform(request)).terminal(this.getTerminal(request))
                 .timestamp(this.getTimestamp(request)).authorities(Collections.emptySet()).build();
     }
 }

+ 5 - 2
framework-security/src/main/java/com/chelvc/framework/security/session/RedisSessionValidator.java

@@ -7,6 +7,7 @@ import java.util.Set;
 import com.chelvc.framework.base.context.ApplicationContextHolder;
 import com.chelvc.framework.base.context.SessionContextHolder;
 import com.chelvc.framework.base.context.Using;
+import com.chelvc.framework.common.model.Platform;
 import com.chelvc.framework.common.util.StringUtils;
 import com.chelvc.framework.redis.context.RedisContextHolder;
 import com.chelvc.framework.redis.context.RedisHashHolder;
@@ -70,13 +71,15 @@ public class RedisSessionValidator extends DefaultSessionValidator {
             }
         }
 
-        // 更新会话主体信息
+        // 更新会话信息
+        String version = this.getVersion(jwt);
+        Platform platform = this.getPlatform(jwt);
         String mobile = (String) context.get(SecurityContextHolder.MOBILE);
         Long creating = (Long) context.get(SecurityContextHolder.CREATING);
         Long registering = (Long) context.get(SecurityContextHolder.REGISTERING);
         Using using = Using.from(RedisUserDailyHashHolder.using(template, id), creating, this.usingRefreshInterval);
         Set<String> authorities = SecurityContextHolder.getAuthorities(jwt);
-        SessionContextHolder.setSession(id, using, scope, mobile, registering, authorities);
+        SessionContextHolder.updateSession(id, using, scope, mobile, version, platform, registering, authorities);
         return OAuth2TokenValidatorResult.success();
     }
 }