Ver Fonte

认证逻辑优化

woody há 1 ano atrás
pai
commit
749164af6f
14 ficheiros alterados com 512 adições e 429 exclusões
  1. 6 6
      framework-base/src/main/java/com/chelvc/framework/base/context/ApplicationContextHolder.java
  2. 144 106
      framework-base/src/main/java/com/chelvc/framework/base/context/SessionContextHolder.java
  3. 12 27
      framework-base/src/main/java/com/chelvc/framework/base/model/Session.java
  4. 4 5
      framework-database/src/main/java/com/chelvc/framework/database/util/EntityUtils.java
  5. 4 3
      framework-feign/src/main/java/com/chelvc/framework/feign/interceptor/FeignHeaderInterceptor.java
  6. 11 11
      framework-redis/src/main/java/com/chelvc/framework/redis/util/RedisUtils.java
  7. 24 50
      framework-security/src/main/java/com/chelvc/framework/security/config/MethodSecurityExpressionWrapper.java
  8. 6 3
      framework-security/src/main/java/com/chelvc/framework/security/config/WebSecurityConfigurer.java
  9. 289 6
      framework-security/src/main/java/com/chelvc/framework/security/context/SecurityContextHolder.java
  10. 1 1
      framework-security/src/main/java/com/chelvc/framework/security/interceptor/TokenReplayValidateInterceptor.java
  11. 5 11
      framework-security/src/main/java/com/chelvc/framework/security/token/RedisTokenActiveValidator.java
  12. 3 3
      framework-security/src/main/java/com/chelvc/framework/security/token/RedisTokenReplayValidator.java
  13. 3 41
      framework-security/src/main/java/com/chelvc/framework/security/token/RedisTokenStore.java
  14. 0 156
      framework-security/src/main/java/com/chelvc/framework/security/util/TokenUtils.java

+ 6 - 6
framework-base/src/main/java/com/chelvc/framework/base/context/ApplicationContextHolder.java

@@ -152,25 +152,25 @@ public class ApplicationContextHolder implements ApplicationContextAware {
      * @return 对象实例
      */
     public static <T> T getBean(Class<T> type) {
-        return getBean(type, false);
+        return getBean(type, true);
     }
 
     /**
      * 获取可以为空的对象实例
      *
      * @param type     对象类型
-     * @param nullable 对象实例是否可以为空
+     * @param required 对象实例是否必须存在
      * @param <T>      对象类型泛型
      * @return 对象实例
      */
-    public static <T> T getBean(Class<T> type, boolean nullable) {
+    public static <T> T getBean(Class<T> type, boolean required) {
         try {
             return getApplicationContext().getBean(type);
         } catch (NoSuchBeanDefinitionException e) {
-            if (nullable) {
-                return null;
+            if (required) {
+                throw e;
             }
-            throw e;
+            return null;
         }
     }
 

+ 144 - 106
framework-base/src/main/java/com/chelvc/framework/base/context/SessionContextHolder.java

@@ -40,14 +40,9 @@ public class SessionContextHolder implements ServletRequestListener {
     public static final String HEADER_ID = "Id";
 
     /**
-     * 会话类型请求头
+     * 电话号码请求头
      */
-    public static final String HEADER_TYPE = "Type";
-
-    /**
-     * 租户标识请求头
-     */
-    public static final String HEADER_TENANT = "Tenant";
+    public static final String HEADER_MOBILE = "Mobile";
 
     /**
      * 设备标识请求头
@@ -55,15 +50,20 @@ public class SessionContextHolder implements ServletRequestListener {
     public static final String HEADER_DEVICE = "Device";
 
     /**
-     * 渠道来源请求头
+     * 租户标识请求头
      */
-    public static final String HEADER_CHANNEL = "Channel";
+    public static final String HEADER_TENANT = "Tenant";
 
     /**
      * 是否首次请求头
      */
     public static final String HEADER_INITIAL = "Initial";
 
+    /**
+     * 渠道来源请求头
+     */
+    public static final String HEADER_CHANNEL = "Channel";
+
     /**
      * 应用平台请求头
      */
@@ -79,6 +79,11 @@ public class SessionContextHolder implements ServletRequestListener {
      */
     public static final String HEADER_VERSION = "Version";
 
+    /**
+     * 业务类型请求头
+     */
+    public static final String HEADER_BUSINESS = "Business";
+
     /**
      * 终端序号请求头
      */
@@ -156,41 +161,41 @@ public class SessionContextHolder implements ServletRequestListener {
     }
 
     /**
-     * 获取当前会话类型
+     * 获取当前会话请求地址
      *
-     * @return 会话类型
+     * @return 请求地址
      */
-    public static Session.Type getType() {
-        return getType(true);
+    public static String getHost() {
+        return getHost(true);
     }
 
     /**
-     * 获取当前会话类型
+     * 获取当前会话请求地址
      *
      * @param requireSession 会话是否是必须
-     * @return 会话类型
+     * @return 请求地址
      */
-    public static Session.Type getType(boolean requireSession) {
-        return ObjectUtils.ifNull(getSession(requireSession), Session::getType);
+    public static String getHost(boolean requireSession) {
+        return ObjectUtils.ifNull(getSession(requireSession), Session::getHost);
     }
 
     /**
-     * 获取当前会话请求地址
+     * 获取当前会话电话号码
      *
-     * @return 请求地址
+     * @return 电话号码
      */
-    public static String getHost() {
-        return getHost(true);
+    public static String getMobile() {
+        return getMobile(true);
     }
 
     /**
-     * 获取当前会话请求地址
+     * 获取当前会话电话号码
      *
      * @param requireSession 会话是否是必须
-     * @return 请求地址
+     * @return 电话号码
      */
-    public static String getHost(boolean requireSession) {
-        return ObjectUtils.ifNull(getSession(requireSession), Session::getHost);
+    public static String getMobile(boolean requireSession) {
+        return ObjectUtils.ifNull(getSession(requireSession), Session::getMobile);
     }
 
     /**
@@ -232,41 +237,41 @@ public class SessionContextHolder implements ServletRequestListener {
     }
 
     /**
-     * 获取当前会话渠道来源
+     * 判断当前会话是否是首次请求
      *
-     * @return 渠道来源
+     * @return true/false
      */
-    public static String getChannel() {
-        return getChannel(true);
+    public static boolean isInitial() {
+        return isInitial(true);
     }
 
     /**
-     * 获取当前会话渠道来源
+     * 判断当前会话是否是首次请求
      *
      * @param requireSession 会话是否是必须
-     * @return 渠道来源
+     * @return true/false
      */
-    public static String getChannel(boolean requireSession) {
-        return ObjectUtils.ifNull(getSession(requireSession), Session::getChannel);
+    public static boolean isInitial(boolean requireSession) {
+        return Boolean.TRUE.equals(ObjectUtils.ifNull(getSession(requireSession), Session::getInitial));
     }
 
     /**
-     * 判断当前会话是否是首次请求
+     * 获取当前会话渠道来源
      *
-     * @return true/false
+     * @return 渠道来源
      */
-    public static boolean isInitial() {
-        return isInitial(true);
+    public static String getChannel() {
+        return getChannel(true);
     }
 
     /**
-     * 判断当前会话是否是首次请求
+     * 获取当前会话渠道来源
      *
      * @param requireSession 会话是否是必须
-     * @return true/false
+     * @return 渠道来源
      */
-    public static boolean isInitial(boolean requireSession) {
-        return Boolean.TRUE.equals(ObjectUtils.ifNull(getSession(requireSession), Session::getInitial));
+    public static String getChannel(boolean requireSession) {
+        return ObjectUtils.ifNull(getSession(requireSession), Session::getChannel);
     }
 
     /**
@@ -326,6 +331,25 @@ public class SessionContextHolder implements ServletRequestListener {
         return ObjectUtils.ifNull(getSession(requireSession), Session::getVersion);
     }
 
+    /**
+     * 获取当前业务类型
+     *
+     * @return 业务类型
+     */
+    public static String getBusiness() {
+        return getBusiness(true);
+    }
+
+    /**
+     * 获取当前业务类型
+     *
+     * @param requireSession 会话是否是必须
+     * @return 业务类型
+     */
+    public static String getBusiness(boolean requireSession) {
+        return ObjectUtils.ifNull(getSession(requireSession), Session::getBusiness);
+    }
+
     /**
      * 获取当前会话签名信息
      *
@@ -559,30 +583,13 @@ public class SessionContextHolder implements ServletRequestListener {
     }
 
     /**
-     * 判断主体角色是否是管理员
+     * 判断是否是指定业务类型
      *
+     * @param business 业务类型
      * @return true/false
      */
-    public static boolean isAdmin() {
-        return ObjectUtils.ifNull(getSession(false), Session::getType) == Session.Type.ADMIN;
-    }
-
-    /**
-     * 判断主体是否是普通用户
-     *
-     * @return true/false
-     */
-    public static boolean isNormal() {
-        return ObjectUtils.ifNull(getSession(false), Session::getType) == Session.Type.NORMAL;
-    }
-
-    /**
-     * 判断主体是否是客户端
-     *
-     * @return true/false
-     */
-    public static boolean isClient() {
-        return ObjectUtils.ifNull(getSession(false), Session::getType) == Session.Type.CLIENT;
+    public static boolean isBusiness(String business) {
+        return StringUtils.nonEmpty(business) && Objects.equals(business, getBusiness(false));
     }
 
     /**
@@ -652,21 +659,41 @@ public class SessionContextHolder implements ServletRequestListener {
         return ObjectUtils.ifNull(attributes, ServletRequestAttributes::getResponse);
     }
 
+    /**
+     * 获取请求参数
+     *
+     * @param name 参数名称
+     * @return 参数值
+     */
+    public static String getParameter(@NonNull String name) {
+        return getParameter(name, value -> value);
+    }
+
+    /**
+     * 获取请求参数
+     *
+     * @param name    参数名称
+     * @param adapter 参数值适配器
+     * @return 参数值
+     */
+    public static <T> T getParameter(@NonNull String name, @NonNull Function<String, T> adapter) {
+        return StringUtils.ifEmpty(ObjectUtils.ifNull(getRequest(), request -> request.getParameter(name)), adapter);
+    }
+
     /**
      * 初始化会话主体
      *
-     * @param id   主体标识
-     * @param type 会话类型
-     * @return 会话信息
+     * @param id       主体标识
+     * @param business 业务类型
+     * @param mobile   电话号码
      */
-    public static Session initializeSessionPrincipal(@NonNull Long id, @NonNull Session.Type type) {
+    public static void initializeSessionPrincipal(@NonNull Long id, @NonNull String business, String mobile) {
         Session current = Objects.requireNonNull(SESSION_CONTEXT.get().poll(), "Session has not been initialized");
-        Session session = Session.builder().id(id).type(type).host(current.getHost()).tenant(current.getTenant())
-                .device(current.getDevice()).channel(current.getChannel()).version(current.getVersion())
-                .initial(current.getInitial()).platform(current.getPlatform()).terminal(current.getTerminal())
-                .signature(current.getSignature()).sequence(current.getSequence()).build();
+        Session session = Session.builder().id(id).host(current.getHost()).mobile(mobile).device(current.getDevice())
+                .tenant(current.getTenant()).initial(current.getInitial()).channel(current.getChannel())
+                .platform(current.getPlatform()).terminal(current.getTerminal()).version(current.getVersion())
+                .business(business).sequence(current.getSequence()).signature(current.getSignature()).build();
         SESSION_CONTEXT.get().push(session);
-        return session;
     }
 
     /**
@@ -694,16 +721,6 @@ public class SessionContextHolder implements ServletRequestListener {
         return StringUtils.ifEmpty(request.getHeader(HEADER_ID), Long::parseLong);
     }
 
-    /**
-     * 获取会话类型
-     *
-     * @param request Http请求对象
-     * @return 会话类型
-     */
-    protected Session.Type getType(HttpServletRequest request) {
-        return StringUtils.ifEmpty(request.getHeader(HEADER_TYPE), Session.Type::valueOf);
-    }
-
     /**
      * 获取客户端主机地址
      *
@@ -715,13 +732,13 @@ public class SessionContextHolder implements ServletRequestListener {
     }
 
     /**
-     * 获取客户端租户标识
+     * 获取会话电话号码
      *
      * @param request Http请求对象
-     * @return 租户标识
+     * @return 电话号码
      */
-    protected Integer getTenant(HttpServletRequest request) {
-        return StringUtils.ifEmpty(request.getHeader(HEADER_TENANT), Integer::parseInt);
+    protected String getMobile(HttpServletRequest request) {
+        return StringUtils.ifEmpty(request.getHeader(HEADER_MOBILE), (String) null);
     }
 
     /**
@@ -735,23 +752,33 @@ public class SessionContextHolder implements ServletRequestListener {
     }
 
     /**
-     * 获取客户端渠道来源
+     * 获取客户端租户标识
      *
      * @param request Http请求对象
-     * @return 渠道来源
+     * @return 租户标识
      */
-    protected String getChannel(HttpServletRequest request) {
-        return StringUtils.ifEmpty(request.getHeader(HEADER_CHANNEL), (String) null);
+    protected Integer getTenant(HttpServletRequest request) {
+        return StringUtils.ifEmpty(request.getHeader(HEADER_TENANT), Integer::parseInt);
     }
 
     /**
-     * 获取客户端版本号
+     * 获取是否首次请求
      *
      * @param request Http请求对象
-     * @return 版本号
+     * @return true/false
      */
-    protected String getVersion(HttpServletRequest request) {
-        return StringUtils.ifEmpty(request.getHeader(HEADER_VERSION), (String) null);
+    protected Boolean getInitial(HttpServletRequest request) {
+        return StringUtils.ifEmpty(request.getHeader(HEADER_INITIAL), Boolean::parseBoolean);
+    }
+
+    /**
+     * 获取客户端渠道来源
+     *
+     * @param request Http请求对象
+     * @return 渠道来源
+     */
+    protected String getChannel(HttpServletRequest request) {
+        return StringUtils.ifEmpty(request.getHeader(HEADER_CHANNEL), (String) null);
     }
 
     /**
@@ -775,23 +802,33 @@ public class SessionContextHolder implements ServletRequestListener {
     }
 
     /**
-     * 获取签名信息
+     * 获取客户端版本号
      *
      * @param request Http请求对象
-     * @return 签名信息
+     * @return 版本号
      */
-    protected String getSignature(HttpServletRequest request) {
-        return StringUtils.ifEmpty(request.getHeader(HEADER_SIGNATURE), (String) null);
+    protected String getVersion(HttpServletRequest request) {
+        return StringUtils.ifEmpty(request.getHeader(HEADER_VERSION), (String) null);
     }
 
     /**
-     * 获取是否首次请求
+     * 获取业务类型
      *
      * @param request Http请求对象
-     * @return true/false
+     * @return 业务类型
      */
-    protected Boolean getInitial(HttpServletRequest request) {
-        return StringUtils.ifEmpty(request.getHeader(HEADER_INITIAL), Boolean::parseBoolean);
+    protected String getBusiness(HttpServletRequest request) {
+        return StringUtils.ifEmpty(request.getHeader(HEADER_BUSINESS), (String) null);
+    }
+
+    /**
+     * 获取签名信息
+     *
+     * @param request Http请求对象
+     * @return 签名信息
+     */
+    protected String getSignature(HttpServletRequest request) {
+        return StringUtils.ifEmpty(request.getHeader(HEADER_SIGNATURE), (String) null);
     }
 
     /**
@@ -807,11 +844,12 @@ public class SessionContextHolder implements ServletRequestListener {
     @Override
     public void requestInitialized(ServletRequestEvent event) {
         HttpServletRequest request = (HttpServletRequest) event.getServletRequest();
-        Session session = Session.builder().id(this.getId(request)).type(this.getType(request))
-                .host(this.getHost(request)).tenant(this.getTenant(request)).device(this.getDevice(request))
-                .channel(this.getChannel(request)).version(this.getVersion(request)).initial(this.getInitial(request))
-                .platform(this.getPlatform(request)).terminal(this.getTerminal(request))
-                .signature(this.getSignature(request)).sequence(this.getSequence(request)).build();
+        Session session = Session.builder().id(this.getId(request)).host(this.getHost(request))
+                .mobile(this.getMobile(request)).device(this.getDevice(request)).tenant(this.getTenant(request))
+                .initial(this.getInitial(request)).channel(this.getChannel(request)).platform(this.getPlatform(request))
+                .terminal(this.getTerminal(request)).version(this.getVersion(request))
+                .business(this.getBusiness(request)).sequence(this.getSequence(request))
+                .signature(this.getSignature(request)).build();
         setSession(session);
     }
 

+ 12 - 27
framework-base/src/main/java/com/chelvc/framework/base/model/Session.java

@@ -24,14 +24,14 @@ public class Session implements Serializable {
     private Long id;
 
     /**
-     * 会话类型
+     * 请求地址
      */
-    private Type type;
+    private String host;
 
     /**
-     * 请求地址
+     * 电话号码
      */
-    private String host;
+    private String mobile;
 
     /**
      * 设备标识
@@ -44,14 +44,14 @@ public class Session implements Serializable {
     private Integer tenant;
 
     /**
-     * 渠道来源
+     * 是否是首次请求
      */
-    private String channel;
+    private Boolean initial;
 
     /**
-     * 是否是首次请求
+     * 渠道来源
      */
-    private Boolean initial;
+    private String channel;
 
     /**
      * 应用平台
@@ -69,9 +69,9 @@ public class Session implements Serializable {
     private String version;
 
     /**
-     * 签名信息
+     * 业务类型
      */
-    private String signature;
+    private String business;
 
     /**
      * 客户端序号
@@ -79,22 +79,7 @@ public class Session implements Serializable {
     private Integer sequence;
 
     /**
-     * 会话类型枚举
+     * 签名信息
      */
-    public enum Type {
-        /**
-         * 管理员
-         */
-        ADMIN,
-
-        /**
-         * 普通用户
-         */
-        NORMAL,
-
-        /**
-         * 客户端
-         */
-        CLIENT;
-    }
+    private String signature;
 }

+ 4 - 5
framework-database/src/main/java/com/chelvc/framework/database/util/EntityUtils.java

@@ -80,20 +80,19 @@ public final class EntityUtils {
      * @return 数据表信息
      */
     public static TableInfo getTableInfo(Class<?> type) {
-        return getTableInfo(type, false);
+        return getTableInfo(type, true);
     }
 
     /**
      * 根据数据模型类型获取数据表信息
      *
      * @param type     数据模型类型
-     * @param nullable 是否可以为空
+     * @param required 表信息是否必须存在
      * @return 数据表信息
      */
-    public static TableInfo getTableInfo(@NonNull Class<?> type, boolean nullable) {
+    public static TableInfo getTableInfo(@NonNull Class<?> type, boolean required) {
         TableInfo table = TableInfoHelper.getTableInfo(type);
-        return nullable ? table :
-                Objects.requireNonNull(table, String.format("Table information not found: %s", type.getName()));
+        return required ? Objects.requireNonNull(table, String.format("Table not found: %s", type.getName())) : table;
     }
 
     /**

+ 4 - 3
framework-feign/src/main/java/com/chelvc/framework/feign/interceptor/FeignHeaderInterceptor.java

@@ -39,14 +39,15 @@ public class FeignHeaderInterceptor implements RequestInterceptor {
         // 重置会话请求头
         Session session = SessionContextHolder.getSession();
         headers.put(SessionContextHolder.HEADER_ID, session.getId());
-        headers.put(SessionContextHolder.HEADER_TYPE, session.getType());
-        headers.put(SessionContextHolder.HEADER_TENANT, session.getTenant());
+        headers.put(SessionContextHolder.HEADER_MOBILE, session.getMobile());
         headers.put(SessionContextHolder.HEADER_DEVICE, session.getDevice());
-        headers.put(SessionContextHolder.HEADER_CHANNEL, session.getChannel());
+        headers.put(SessionContextHolder.HEADER_TENANT, session.getTenant());
         headers.put(SessionContextHolder.HEADER_INITIAL, session.getInitial());
+        headers.put(SessionContextHolder.HEADER_CHANNEL, session.getChannel());
         headers.put(SessionContextHolder.HEADER_PLATFORM, session.getPlatform());
         headers.put(SessionContextHolder.HEADER_TERMINAL, session.getTerminal());
         headers.put(SessionContextHolder.HEADER_VERSION, session.getVersion());
+        headers.put(SessionContextHolder.HEADER_BUSINESS, session.getBusiness());
         headers.put(SessionContextHolder.HEADER_SEQUENCE, session.getSequence());
         headers.put(SessionContextHolder.HEADER_SIGNATURE, session.getSignature());
 

+ 11 - 11
framework-redis/src/main/java/com/chelvc/framework/redis/util/RedisUtils.java

@@ -254,16 +254,6 @@ public class RedisUtils implements ApplicationContextAware {
         }, e -> false);
     }
 
-    /**
-     * 获取锁标识信息
-     *
-     * @param names 锁名称列表
-     * @return 锁名称/标识信息映射表
-     */
-    public static Map<String, String> locks(@NonNull List<String> names) {
-        return get(names, key -> key, value -> new String(value, StandardCharsets.UTF_8));
-    }
-
     /**
      * 尝试加锁(无阻塞),如果加锁成功则返回锁标识,否则返回null
      *
@@ -562,6 +552,16 @@ public class RedisUtils implements ApplicationContextAware {
         return object;
     }
 
+    /**
+     * 批量获取Redis值
+     *
+     * @param keys 键名称列表
+     * @return 键/值映射表
+     */
+    public static Map<String, String> get(@NonNull List<String> keys) {
+        return get(keys, key -> key, value -> new String(value, StandardCharsets.UTF_8));
+    }
+
     /**
      * 批量获取Redis值
      *
@@ -570,7 +570,7 @@ public class RedisUtils implements ApplicationContextAware {
      * @param valueAdapter 值适配器
      * @param <K>          键类型
      * @param <V>          值类型
-     * @return 锁名称/标识信息映射表
+     * @return 键/值映射表
      */
     public static <K, V> Map<K, V> get(List<String> keys, @NonNull Function<String, K> keyAdapter,
                                        @NonNull Function<byte[], V> valueAdapter) {

+ 24 - 50
framework-security/src/main/java/com/chelvc/framework/security/config/MethodSecurityExpressionWrapper.java

@@ -6,10 +6,9 @@ import java.util.stream.Stream;
 
 import com.chelvc.framework.base.context.SessionContextHolder;
 import com.chelvc.framework.base.model.Platform;
-import com.chelvc.framework.base.model.Session;
 import com.chelvc.framework.base.model.Terminal;
+import com.chelvc.framework.base.util.ObjectUtils;
 import com.chelvc.framework.security.context.SecurityContextHolder;
-import com.chelvc.framework.security.util.TokenUtils;
 import org.springframework.security.access.PermissionEvaluator;
 import org.springframework.security.access.expression.SecurityExpressionRoot;
 import org.springframework.security.access.expression.method.MethodSecurityExpressionOperations;
@@ -70,48 +69,6 @@ public class MethodSecurityExpressionWrapper implements MethodSecurityExpression
         return this.root.getAuthentication();
     }
 
-    /**
-     * 判断是否是指定会话类型
-     *
-     * @param types 会话类型数组
-     * @return true/false
-     */
-    public boolean isType(Session.Type... types) {
-        Session.Type current;
-        if (types == null || types.length == 0
-                || (current = TokenUtils.getType(SecurityContextHolder.getJwt(true))) == null) {
-            return false;
-        }
-        return Stream.of(types).filter(Objects::nonNull).anyMatch(type -> Objects.equals(type, current));
-    }
-
-    /**
-     * 判断主体是否是管理员
-     *
-     * @return true/false
-     */
-    public boolean isAdmin() {
-        return TokenUtils.getType(SecurityContextHolder.getJwt(true)) == Session.Type.ADMIN;
-    }
-
-    /**
-     * 判断主体是否是普通用户
-     *
-     * @return true/false
-     */
-    public boolean isNormal() {
-        return TokenUtils.getType(SecurityContextHolder.getJwt(true)) == Session.Type.NORMAL;
-    }
-
-    /**
-     * 判断主体是否是客户端
-     *
-     * @return true/false
-     */
-    public boolean isClient() {
-        return TokenUtils.getType(SecurityContextHolder.getJwt(true)) == Session.Type.CLIENT;
-    }
-
     /**
      * 判断主体是否是指定账户
      *
@@ -122,18 +79,35 @@ public class MethodSecurityExpressionWrapper implements MethodSecurityExpression
         if (accounts == null || accounts.length == 0) {
             return false;
         }
-        Jwt jwt = SecurityContextHolder.getJwt(true);
+        Jwt jwt = SecurityContextHolder.getJwt(false);
         if (jwt == null) {
             return false;
         }
-        Long id = TokenUtils.getId(jwt);
-        String user = TokenUtils.getUser(jwt);
-        String client = TokenUtils.getClient(jwt);
-        return Stream.of(accounts).filter(Objects::nonNull).anyMatch(account ->
-                Objects.equals(account, id) || Objects.equals(account, user) || Objects.equals(account, client)
+        String id = ObjectUtils.ifNull(SecurityContextHolder.getId(jwt), String::valueOf);
+        String user = SecurityContextHolder.getUser(jwt);
+        String mobile = SecurityContextHolder.getMobile(jwt);
+        String client = SecurityContextHolder.getClient(jwt);
+        return Stream.of(accounts).filter(Objects::nonNull).map(String::valueOf).anyMatch(account ->
+                Objects.equals(account, id) || Objects.equals(account, user)
+                        || Objects.equals(account, mobile) || Objects.equals(account, client)
         );
     }
 
+    /**
+     * 判断是否是指定业务类型
+     *
+     * @param businesses 业务类型数组
+     * @return true/false
+     */
+    public boolean isBusiness(String... businesses) {
+        if (businesses == null || businesses.length == 0) {
+            return false;
+        }
+        String current = SecurityContextHolder.getBusiness();
+        return current != null && Stream.of(businesses).filter(Objects::nonNull)
+                .anyMatch(business -> Objects.equals(business, current));
+    }
+
     /**
      * 判断当前请求是否为指定客户端平台
      *

+ 6 - 3
framework-security/src/main/java/com/chelvc/framework/security/config/WebSecurityConfigurer.java

@@ -14,7 +14,6 @@ import com.chelvc.framework.base.util.HttpUtils;
 import com.chelvc.framework.base.util.StringUtils;
 import com.chelvc.framework.security.context.SecurityContextHolder;
 import com.chelvc.framework.security.token.TokenActiveValidator;
-import com.chelvc.framework.security.util.TokenUtils;
 import com.google.common.collect.Lists;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -132,7 +131,11 @@ public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
             public OAuth2TokenValidatorResult validate(Jwt jwt) {
                 // 设置令牌/认证主体上下文
                 SecurityContextHolder.setJwt(jwt);
-                SessionContextHolder.initializeSessionPrincipal(TokenUtils.getId(jwt), TokenUtils.getType(jwt));
+                SessionContextHolder.initializeSessionPrincipal(
+                        SecurityContextHolder.getId(jwt),
+                        SecurityContextHolder.getBusiness(jwt),
+                        SecurityContextHolder.getMobile(jwt)
+                );
                 return this.timestampValidator.validate(jwt);
             }
         });
@@ -149,7 +152,7 @@ public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
         JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
         JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
         jwtGrantedAuthoritiesConverter.setAuthorityPrefix(StringUtils.EMPTY);
-        jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName(TokenUtils.JWT_CLAIM_AUTHORITIES);
+        jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName(SecurityContextHolder.JWT_CLAIM_AUTHORITIES);
         jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);
         return jwtAuthenticationConverter;
     }

+ 289 - 6
framework-security/src/main/java/com/chelvc/framework/security/context/SecurityContextHolder.java

@@ -1,5 +1,6 @@
 package com.chelvc.framework.security.context;
 
+import java.io.Serializable;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
@@ -16,10 +17,13 @@ import com.chelvc.framework.base.context.ApplicationContextHolder;
 import com.chelvc.framework.base.context.JacksonContextHolder;
 import com.chelvc.framework.base.context.SessionContextHolder;
 import com.chelvc.framework.base.model.Session;
+import com.chelvc.framework.base.util.ObjectUtils;
 import com.chelvc.framework.base.util.StringUtils;
 import com.chelvc.framework.security.config.SecurityProperties;
-import com.chelvc.framework.security.util.TokenUtils;
 import com.fasterxml.jackson.core.type.TypeReference;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import lombok.NonNull;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.codec.digest.HmacAlgorithms;
@@ -29,6 +33,8 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationContextAware;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.security.oauth2.common.OAuth2AccessToken;
 import org.springframework.security.oauth2.jwt.Jwt;
 import org.springframework.util.CollectionUtils;
 
@@ -44,6 +50,41 @@ import org.springframework.util.CollectionUtils;
 @Configuration
 @RequiredArgsConstructor(onConstructor = @__(@Autowired))
 public class SecurityContextHolder implements ApplicationContextAware, ServletRequestListener {
+    /**
+     * 主体身份标识
+     */
+    public static final String JWT_CLAIM_ID = "id";
+
+    /**
+     * JWT身份标识
+     */
+    public static final String JWT_CLAIM_JTI = "jti";
+
+    /**
+     * JWT手机号标识
+     */
+    public static final String JWT_CLAIM_MOBILE = "mobile";
+
+    /**
+     * 业务类型标识
+     */
+    public static final String JWT_CLAIM_BUSINESS = "business";
+
+    /**
+     * 用户账号标识
+     */
+    public static final String JWT_CLAIM_USER_NAME = "user_name";
+
+    /**
+     * 客户端账号标识
+     */
+    public static final String JWT_CLAIM_CLIENT_ID = "client_id";
+
+    /**
+     * 用户权限标识
+     */
+    public static final String JWT_CLAIM_AUTHORITIES = "authorities";
+
     /**
      * 权限属性名称
      */
@@ -82,7 +123,7 @@ public class SecurityContextHolder implements ApplicationContextAware, ServletRe
      * @return JWT信息
      */
     public static Jwt getJwt() {
-        return getJwt(false);
+        return getJwt(true);
     }
 
     /**
@@ -97,12 +138,254 @@ public class SecurityContextHolder implements ApplicationContextAware, ServletRe
     /**
      * 获取JWT信息
      *
-     * @param nullable JWT是否可以为空
+     * @param required JWT是否必须存在
      * @return JWT信息
      */
-    public static Jwt getJwt(boolean nullable) {
+    public static Jwt getJwt(boolean required) {
         Jwt jwt = JWT_CONTEXT.get();
-        return nullable ? jwt : Objects.requireNonNull(jwt, "Jwt has not been initialized");
+        return required ? Objects.requireNonNull(jwt, "Jwt has not been initialized") : jwt;
+    }
+
+    /**
+     * 获取主体身份
+     *
+     * @return 主体身份标识
+     */
+    public static Long getId() {
+        return getId(getJwt(false));
+    }
+
+    /**
+     * 获取主体身份
+     *
+     * @param jwt JWT对象
+     * @return 主体身份标识
+     */
+    public static Long getId(Jwt jwt) {
+        return ObjectUtils.ifNull(jwt, t -> ObjectUtils.object2long(t.getClaim(JWT_CLAIM_ID)));
+    }
+
+    /**
+     * 获取主体身份
+     *
+     * @param token OAuth2访问令牌对象
+     * @return 主体身份标识
+     */
+    public static Long getId(OAuth2AccessToken token) {
+        return ObjectUtils.ifNull(
+                ObjectUtils.ifNull(token, OAuth2AccessToken::getAdditionalInformation),
+                info -> ObjectUtils.object2long(info.get(JWT_CLAIM_ID))
+        );
+    }
+
+    /**
+     * 获取JWT身份标识
+     *
+     * @return JWT身份标识
+     */
+    public static String getJti() {
+        return getJti(getJwt(false));
+    }
+
+    /**
+     * 获取JWT身份标识
+     *
+     * @param jwt JWT对象
+     * @return JWT身份标识
+     */
+    public static String getJti(Jwt jwt) {
+        return jwt == null ? null : StringUtils.ifEmpty(jwt.getClaimAsString(JWT_CLAIM_JTI), (String) null);
+    }
+
+    /**
+     * 获取用户标识
+     *
+     * @return 用户标识
+     */
+    public static String getUser() {
+        return getUser(getJwt(false));
+    }
+
+    /**
+     * 获取用户标识
+     *
+     * @param jwt JWT对象
+     * @return 用户标识
+     */
+    public static String getUser(Jwt jwt) {
+        return jwt == null ? null : StringUtils.ifEmpty(jwt.getClaimAsString(JWT_CLAIM_USER_NAME), (String) null);
+    }
+
+    /**
+     * 获取手机号
+     *
+     * @return 手机号
+     */
+    public static String getMobile() {
+        return getMobile(getJwt(false));
+    }
+
+    /**
+     * 获取手机号
+     *
+     * @param jwt JWT对象
+     * @return 手机号
+     */
+    public static String getMobile(Jwt jwt) {
+        return jwt == null ? null : StringUtils.ifEmpty(jwt.getClaimAsString(JWT_CLAIM_MOBILE), (String) null);
+    }
+
+    /**
+     * 获取手机号
+     *
+     * @param token OAuth2访问令牌对象
+     * @return 手机号
+     */
+    public static String getMobile(OAuth2AccessToken token) {
+        return ObjectUtils.ifNull(
+                ObjectUtils.ifNull(token, OAuth2AccessToken::getAdditionalInformation),
+                info -> StringUtils.ifEmpty((String) info.get(JWT_CLAIM_MOBILE), (String) null)
+        );
+    }
+
+    /**
+     * 获取业务类型
+     *
+     * @return 业务类型
+     */
+    public static String getBusiness() {
+        return getBusiness(getJwt(false));
+    }
+
+    /**
+     * 获取业务类型
+     *
+     * @param jwt JWT对象
+     * @return 业务类型
+     */
+    public static String getBusiness(Jwt jwt) {
+        return jwt == null ? null : StringUtils.ifEmpty(jwt.getClaimAsString(JWT_CLAIM_BUSINESS), (String) null);
+    }
+
+    /**
+     * 获取业务类型
+     *
+     * @param id            会话ID
+     * @param redisTemplate Redis操作模版实例
+     * @return 业务类型
+     */
+    public static String getBusiness(@NonNull Serializable id, @NonNull RedisTemplate<String, Object> redisTemplate) {
+        return (String) redisTemplate.opsForHash().get(buildAccessTokenKey(id), "business");
+    }
+
+    /**
+     * 获取业务类型
+     *
+     * @param token OAuth2访问令牌对象
+     * @return 业务类型
+     */
+    public static String getBusiness(OAuth2AccessToken token) {
+        return ObjectUtils.ifNull(
+                ObjectUtils.ifNull(token, OAuth2AccessToken::getAdditionalInformation),
+                info -> StringUtils.ifEmpty((String) info.get(JWT_CLAIM_BUSINESS), (String) null)
+        );
+    }
+
+    /**
+     * 获取客户端标识
+     *
+     * @return 客户端标识
+     */
+    public static String getClient() {
+        return getClient(getJwt(false));
+    }
+
+    /**
+     * 获取客户端标识
+     *
+     * @param jwt JWT对象
+     * @return 客户端标识
+     */
+    public static String getClient(Jwt jwt) {
+        return jwt == null ? null : StringUtils.ifEmpty(jwt.getClaimAsString(JWT_CLAIM_CLIENT_ID), (String) null);
+    }
+
+    /**
+     * 获取用户权限集合
+     *
+     * @return 权限标识集合
+     */
+    public static Set<String> getAuthorities() {
+        return getAuthorities(getJwt(false));
+    }
+
+    /**
+     * 获取用户权限集合
+     *
+     * @param jwt JWT对象
+     * @return 权限标识集合
+     */
+    public static Set<String> getAuthorities(Jwt jwt) {
+        List<String> authorities = jwt == null ? null : jwt.getClaimAsStringList(JWT_CLAIM_AUTHORITIES);
+        return CollectionUtils.isEmpty(authorities) ? Collections.emptySet() : Sets.newHashSet(authorities);
+    }
+
+    /**
+     * 构建令牌标识
+     *
+     * @param id 会话ID
+     * @return 令牌标识
+     */
+    public static String buildAccessTokenKey(@NonNull Serializable id) {
+        return String.format("token:%s:%s", id, SessionContextHolder.getTerminal(false));
+    }
+
+    /**
+     * 存储访问令牌
+     *
+     * @param token         token OAuth2访问令牌对象
+     * @param redisTemplate Redis操作模版实例
+     */
+    public static void storeAccessToken(@NonNull OAuth2AccessToken token,
+                                        @NonNull RedisTemplate<String, Object> redisTemplate) {
+        String key = buildAccessTokenKey(getId(token));
+        if (log.isDebugEnabled()) {
+            log.debug("Saving redis token: {}", key);
+        }
+        Map<String, Object> context = Maps.newHashMapWithExpectedSize(2);
+        context.put("value", token.getValue());
+        String business = getBusiness(token);
+        if (StringUtils.nonEmpty(business)) {
+            context.put("business", business);
+        }
+        redisTemplate.opsForHash().putAll(key, context);
+        redisTemplate.expireAt(key, token.getExpiration());
+    }
+
+    /**
+     * 删除访问令牌
+     *
+     * @param token         token OAuth2访问令牌对象
+     * @param redisTemplate Redis操作模版实例
+     */
+    public static void removeAccessToken(@NonNull OAuth2AccessToken token,
+                                         @NonNull RedisTemplate<String, Object> redisTemplate) {
+        String key = buildAccessTokenKey(getId(token));
+        if (log.isDebugEnabled()) {
+            log.debug("Saving redis token: {}", key);
+        }
+        redisTemplate.delete(key);
+    }
+
+    /**
+     * 获取访问令牌
+     *
+     * @param jwt           JWT实例
+     * @param redisTemplate Redis操作模版实例
+     * @return 访问令牌
+     */
+    public static String getAccessToken(@NonNull Jwt jwt, @NonNull RedisTemplate<String, Object> redisTemplate) {
+        return (String) redisTemplate.opsForHash().get(buildAccessTokenKey(getId(jwt)), "value");
     }
 
     /**
@@ -129,7 +412,7 @@ public class SecurityContextHolder implements ApplicationContextAware, ServletRe
         }
 
         // 基于用户角色及对应的资源判断是否拥有权限
-        Set<String> roles = TokenUtils.getAuthorities(getJwt(true));
+        Set<String> roles = getAuthorities(getJwt(false));
         if (CollectionUtils.isEmpty(roles)) {
             return false;
         }

+ 1 - 1
framework-security/src/main/java/com/chelvc/framework/security/interceptor/TokenReplayValidateInterceptor.java

@@ -57,7 +57,7 @@ public class TokenReplayValidateInterceptor implements HandlerInterceptor, WebMv
     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
         // 防重放验证
         if (handler instanceof HandlerMethod && !this.isRepeatableMethod((HandlerMethod) handler)) {
-            Jwt jwt = SecurityContextHolder.getJwt(true);
+            Jwt jwt = SecurityContextHolder.getJwt(false);
             if (jwt == null) {
                 throw new BusinessException(HttpStatus.UNAUTHORIZED, "Token unspecified");
             } else if (this.tokenReplayValidator.isReplayed(request, (HandlerMethod) handler, jwt)) {

+ 5 - 11
framework-security/src/main/java/com/chelvc/framework/security/token/RedisTokenActiveValidator.java

@@ -2,11 +2,10 @@ package com.chelvc.framework.security.token;
 
 import java.util.Objects;
 
-import com.chelvc.framework.base.context.SessionContextHolder;
-import com.chelvc.framework.base.model.Terminal;
 import com.chelvc.framework.base.util.StringUtils;
+import com.chelvc.framework.redis.util.RedisUtils;
 import com.chelvc.framework.security.config.WebSecurityConfigurer;
-import com.chelvc.framework.security.util.TokenUtils;
+import com.chelvc.framework.security.context.SecurityContextHolder;
 import lombok.NonNull;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.data.redis.core.RedisTemplate;
@@ -37,15 +36,10 @@ public class RedisTokenActiveValidator implements TokenActiveValidator {
     public OAuth2TokenValidatorResult validate(@NonNull Jwt jwt) {
         // 验证Redis中是否存在该令牌,同时忽略Redis请求/操作异常(为了提高系统可用性)
         String token = null;
-        Terminal terminal = SessionContextHolder.getTerminal(false);
-        String key = TokenUtils.key(terminal, TokenUtils.getId(jwt));
-        if (log.isDebugEnabled()) {
-            log.debug("Validating redis token key: {}", key);
-        }
         try {
-            if (StringUtils.isEmpty(token = (String) this.redisTemplate.opsForValue().get(key))) {
+            if (StringUtils.isEmpty(token = SecurityContextHolder.getAccessToken(jwt, this.redisTemplate))) {
                 return OAuth2TokenValidatorResult.failure(
-                        new OAuth2Error("invalid_token", "Token is not active", null)
+                        new OAuth2Error("invalid_token", "Token is expired", null)
                 );
             }
         } catch (Exception e) {
@@ -55,7 +49,7 @@ public class RedisTokenActiveValidator implements TokenActiveValidator {
         // 判断令牌是否相同,如果不相同则标识令牌已经被重置(账号已在其他地方登录)
         if (!StringUtils.isEmpty(token) && !Objects.equals(token, jwt.getTokenValue())) {
             throw new OAuth2AuthenticationException(
-                    new BearerTokenError("invalid_token", HttpStatus.GONE, "Token has been reset", null)
+                    new BearerTokenError("invalid_token", HttpStatus.GONE, "Token is reset", null)
             );
         }
         return OAuth2TokenValidatorResult.success();

+ 3 - 3
framework-security/src/main/java/com/chelvc/framework/security/token/RedisTokenReplayValidator.java

@@ -4,8 +4,8 @@ import java.util.Objects;
 import java.util.concurrent.TimeUnit;
 import javax.servlet.http.HttpServletRequest;
 
+import com.chelvc.framework.security.context.SecurityContextHolder;
 import com.chelvc.framework.security.interceptor.TokenReplayValidateInterceptor;
-import com.chelvc.framework.security.util.TokenUtils;
 import lombok.NonNull;
 import org.apache.commons.codec.digest.DigestUtils;
 import org.springframework.data.redis.core.RedisTemplate;
@@ -38,9 +38,9 @@ public class RedisTokenReplayValidator implements TokenReplayValidator {
                 request.getServerName(),
                 method.getBeanType().getName(),
                 method.getMethod().getName(),
-                TokenUtils.getJti(jwt)
+                SecurityContextHolder.getJti(jwt)
         ));
-        Long id = TokenUtils.getId(jwt);
+        Long id = SecurityContextHolder.getId(jwt);
         long timeout = Objects.requireNonNull(jwt.getExpiresAt()).toEpochMilli() - System.currentTimeMillis();
         ValueOperations<String, Object> operations = this.redisTemplate.opsForValue();
         return Boolean.FALSE.equals(operations.setIfAbsent(key, id, timeout, TimeUnit.MILLISECONDS));

+ 3 - 41
framework-security/src/main/java/com/chelvc/framework/security/token/RedisTokenStore.java

@@ -1,11 +1,6 @@
 package com.chelvc.framework.security.token;
 
-import java.util.Date;
-import java.util.concurrent.TimeUnit;
-
-import com.chelvc.framework.base.context.SessionContextHolder;
-import com.chelvc.framework.base.model.Terminal;
-import com.chelvc.framework.security.util.TokenUtils;
+import com.chelvc.framework.security.context.SecurityContextHolder;
 import lombok.NonNull;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.data.redis.core.RedisTemplate;
@@ -30,46 +25,13 @@ public class RedisTokenStore extends JwtTokenStore {
         this.redisTemplate = redisTemplate;
     }
 
-    /**
-     * 保存令牌信息
-     *
-     * @param terminal   客户终端
-     * @param id         主体标识
-     * @param token      令牌标识
-     * @param expiration 过期时间
-     */
-    protected void save(Terminal terminal, Long id, String token, Date expiration) {
-        String key = TokenUtils.key(terminal, id);
-        if (log.isDebugEnabled()) {
-            log.debug("Saving redis token key: {}", key);
-        }
-        long timeout = expiration.getTime() - System.currentTimeMillis();
-        this.redisTemplate.opsForValue().set(key, token, timeout, TimeUnit.MILLISECONDS);
-    }
-
-    /**
-     * 移除令牌
-     *
-     * @param terminal 客户终端
-     * @param id       主体标识
-     */
-    protected void remove(Terminal terminal, Long id) {
-        String key = TokenUtils.key(terminal, id);
-        if (log.isDebugEnabled()) {
-            log.debug("Removing redis token key: {}", key);
-        }
-        this.redisTemplate.delete(key);
-    }
-
     @Override
     public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
-        Terminal terminal = SessionContextHolder.getTerminal(false);
-        this.save(terminal, TokenUtils.getId(token), token.getValue(), token.getExpiration());
+        SecurityContextHolder.storeAccessToken(token, this.redisTemplate);
     }
 
     @Override
     public void removeAccessToken(OAuth2AccessToken token) {
-        Terminal terminal = SessionContextHolder.getTerminal(false);
-        this.remove(terminal, TokenUtils.getId(token));
+        SecurityContextHolder.removeAccessToken(token, this.redisTemplate);
     }
 }

+ 0 - 156
framework-security/src/main/java/com/chelvc/framework/security/util/TokenUtils.java

@@ -1,156 +0,0 @@
-package com.chelvc.framework.security.util;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-
-import com.chelvc.framework.base.model.Session;
-import com.chelvc.framework.base.model.Terminal;
-import com.chelvc.framework.base.util.ObjectUtils;
-import com.chelvc.framework.base.util.StringUtils;
-import com.google.common.collect.Sets;
-import org.springframework.security.oauth2.common.OAuth2AccessToken;
-import org.springframework.security.oauth2.jwt.Jwt;
-import org.springframework.util.CollectionUtils;
-
-/**
- * 令牌工具类
- *
- * @author Woody
- * @date 2023/4/5
- */
-public final class TokenUtils {
-    /**
-     * 主体身份标识
-     */
-    public static final String JWT_CLAIM_ID = "id";
-
-    /**
-     * JWT身份标识
-     */
-    public static final String JWT_CLAIM_JTI = "jti";
-
-    /**
-     * 会话类型标识
-     */
-    public static final String JWT_CLAIM_TYPE = "type";
-
-    /**
-     * JWT手机号标识
-     */
-    public static final String JWT_CLAIM_MOBILE = "mobile";
-
-    /**
-     * 用户账号标识
-     */
-    public static final String JWT_CLAIM_USER_NAME = "user_name";
-
-    /**
-     * 客户端账号标识
-     */
-    public static final String JWT_CLAIM_CLIENT_ID = "client_id";
-
-    /**
-     * 用户权限标识
-     */
-    public static final String JWT_CLAIM_AUTHORITIES = "authorities";
-
-    private TokenUtils() {
-    }
-
-    /**
-     * 获取主体身份
-     *
-     * @param jwt JWT对象
-     * @return 主体身份标识
-     */
-    public static Long getId(Jwt jwt) {
-        return ObjectUtils.ifNull(jwt, t -> ObjectUtils.object2long(t.getClaim(JWT_CLAIM_ID)));
-    }
-
-    /**
-     * 获取主体身份
-     *
-     * @param token OAuth2访问令牌对象
-     * @return 主体身份标识
-     */
-    public static Long getId(OAuth2AccessToken token) {
-        return ObjectUtils.ifNull(
-                ObjectUtils.ifNull(token, OAuth2AccessToken::getAdditionalInformation),
-                info -> ObjectUtils.object2long(info.get(JWT_CLAIM_ID))
-        );
-    }
-
-    /**
-     * 获取JWT身份标识
-     *
-     * @param jwt JWT对象
-     * @return JWT身份标识
-     */
-    public static String getJti(Jwt jwt) {
-        return jwt == null ? null : jwt.getClaimAsString(JWT_CLAIM_JTI);
-    }
-
-    /**
-     * 获取会话类型
-     *
-     * @param jwt JWT对象
-     * @return 会话类型
-     */
-    public static Session.Type getType(Jwt jwt) {
-        String type = jwt == null ? null : jwt.getClaimAsString(JWT_CLAIM_TYPE);
-        return StringUtils.isEmpty(type) ? null : Session.Type.valueOf(type);
-    }
-
-    /**
-     * 获取用户标识
-     *
-     * @param jwt JWT对象
-     * @return 用户标识
-     */
-    public static String getUser(Jwt jwt) {
-        return jwt == null ? null : jwt.getClaimAsString(JWT_CLAIM_USER_NAME);
-    }
-
-    /**
-     * 获取手机号
-     *
-     * @param jwt JWT对象
-     * @return 手机号
-     */
-    public static String getMobile(Jwt jwt) {
-        return jwt == null ? null : jwt.getClaimAsString(JWT_CLAIM_MOBILE);
-    }
-
-    /**
-     * 获取客户端标识
-     *
-     * @param jwt JWT对象
-     * @return 客户端标识
-     */
-    public static String getClient(Jwt jwt) {
-        return jwt == null ? null : jwt.getClaimAsString(JWT_CLAIM_CLIENT_ID);
-    }
-
-    /**
-     * 获取用户权限集合
-     *
-     * @param jwt JWT对象
-     * @return 权限标识集合
-     */
-    public static Set<String> getAuthorities(Jwt jwt) {
-        List<String> authorities = jwt == null ? null : jwt.getClaimAsStringList(JWT_CLAIM_AUTHORITIES);
-        return CollectionUtils.isEmpty(authorities) ? Collections.emptySet() : Sets.newHashSet(authorities);
-    }
-
-    /**
-     * 构建令牌缓存标识(访问端+主体标识)
-     *
-     * @param terminal 客户终端
-     * @param id       主体标识
-     * @return 标识
-     */
-    public static String key(Terminal terminal, Long id) {
-        return String.format("token:%s:%d", terminal, id);
-    }
-}