From 644866a392f06bbec063c6846ea5946aaba1e172 Mon Sep 17 00:00:00 2001 From: Carina Date: Thu, 17 Mar 2022 16:31:10 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E7=99=BB=E5=BD=95Token=E8=8E=B7=E5=8F=96=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../opsli/common/thread/AsyncProcessor.java | 1 - .../common/thread/ThreadPoolFactory.java | 18 +++--- .../conf/SpringWebMvcConfig.java | 8 +++ .../aspect/{TokenAop.java => LogAop.java} | 45 +------------- .../interceptor/UserAuthInterceptor.java | 61 +++++++++++++++++++ .../org/opsli/core/general/StartPrint.java | 17 +++++- .../UserContextHolder.java} | 59 +++++++++++------- .../ApplicationFailedEventListener.java | 2 +- .../core/security/shiro/realm/JwtRealm.java | 13 ++-- .../org/opsli/core/utils/UserTokenUtil.java | 15 +++-- .../java/org/opsli/core/utils/UserUtil.java | 8 ++- .../system/login/web/LoginRestController.java | 25 +++++--- pom.xml | 6 ++ 13 files changed, 181 insertions(+), 97 deletions(-) rename opsli-base-support/opsli-core/src/main/java/org/opsli/core/filters/aspect/{TokenAop.java => LogAop.java} (57%) create mode 100644 opsli-base-support/opsli-core/src/main/java/org/opsli/core/filters/interceptor/UserAuthInterceptor.java rename opsli-base-support/opsli-core/src/main/java/org/opsli/core/{api/TokenThreadLocal.java => holder/UserContextHolder.java} (60%) diff --git a/opsli-base-support/opsli-common/src/main/java/org/opsli/common/thread/AsyncProcessor.java b/opsli-base-support/opsli-common/src/main/java/org/opsli/common/thread/AsyncProcessor.java index 2f7e86b1..c6ccaa78 100644 --- a/opsli-base-support/opsli-common/src/main/java/org/opsli/common/thread/AsyncProcessor.java +++ b/opsli-base-support/opsli-common/src/main/java/org/opsli/common/thread/AsyncProcessor.java @@ -5,7 +5,6 @@ import com.google.common.util.concurrent.*; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.opsli.common.thread.ThreadPoolFactory; import java.util.concurrent.Callable; import java.util.concurrent.RejectedExecutionException; diff --git a/opsli-base-support/opsli-common/src/main/java/org/opsli/common/thread/ThreadPoolFactory.java b/opsli-base-support/opsli-common/src/main/java/org/opsli/common/thread/ThreadPoolFactory.java index 74e68925..15031c85 100644 --- a/opsli-base-support/opsli-common/src/main/java/org/opsli/common/thread/ThreadPoolFactory.java +++ b/opsli-base-support/opsli-common/src/main/java/org/opsli/common/thread/ThreadPoolFactory.java @@ -1,12 +1,10 @@ package org.opsli.common.thread; +import com.alibaba.ttl.threadpool.TtlExecutors; import com.google.common.util.concurrent.ThreadFactoryBuilder; -import java.util.concurrent.LinkedBlockingDeque; -import java.util.concurrent.RejectedExecutionHandler; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; /** * 线程池工厂 @@ -41,7 +39,7 @@ public final class ThreadPoolFactory { * 创建默认的线程池 * @return ThreadPoolExecutor */ - public static ThreadPoolExecutor createDefThreadPool(){ + public static ExecutorService createDefThreadPool(){ return createInitThreadPool(DEFAULT_MAX_CONCURRENT, DEFAULT_MAX_CONCURRENT * 4, DEFAULT_KEEP_ALIVE, TimeUnit.SECONDS, DEFAULT_SIZE, DEFAULT_THREAD_POOL_NAME, new ThreadPoolExecutor.CallerRunsPolicy()); } @@ -53,7 +51,7 @@ public final class ThreadPoolFactory { * @param poolName 线程池名称 * @return ThreadPoolExecutor */ - public static ThreadPoolExecutor createDefThreadPool(String poolName){ + public static ExecutorService createDefThreadPool(String poolName){ return createInitThreadPool(DEFAULT_MAX_CONCURRENT, DEFAULT_MAX_CONCURRENT * 4, DEFAULT_KEEP_ALIVE, TimeUnit.SECONDS, DEFAULT_SIZE, poolName, new ThreadPoolExecutor.CallerRunsPolicy()); } @@ -65,7 +63,7 @@ public final class ThreadPoolFactory { * @param poolName 线程池名称 * @return ThreadPoolExecutor */ - public static ThreadPoolExecutor createDefThreadPool(int maxConcurrent, String poolName){ + public static ExecutorService createDefThreadPool(int maxConcurrent, String poolName){ return createInitThreadPool(maxConcurrent, maxConcurrent * 4, DEFAULT_KEEP_ALIVE, TimeUnit.SECONDS, DEFAULT_SIZE, poolName, new ThreadPoolExecutor.CallerRunsPolicy()); } @@ -81,7 +79,7 @@ public final class ThreadPoolFactory { * @param handler 拒绝处理策略 * @return ThreadPoolExecutor */ - public static ThreadPoolExecutor createInitThreadPool(final int coreConcurrent, + public static ExecutorService createInitThreadPool(final int coreConcurrent, final int maxConcurrent, final long keepAlive, final TimeUnit timeUnit, @@ -89,11 +87,11 @@ public final class ThreadPoolFactory { final String poolName, final RejectedExecutionHandler handler ){ - return new ThreadPoolExecutor(coreConcurrent, maxConcurrent, keepAlive, timeUnit, + return TtlExecutors.getTtlExecutorService(new ThreadPoolExecutor(coreConcurrent, maxConcurrent, keepAlive, timeUnit, new LinkedBlockingDeque<>(queueSize), new ThreadFactoryBuilder().setNameFormat(poolName).build(), handler - ); + )); } private ThreadPoolFactory(){} diff --git a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/autoconfigure/conf/SpringWebMvcConfig.java b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/autoconfigure/conf/SpringWebMvcConfig.java index 07708ba2..0294838c 100644 --- a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/autoconfigure/conf/SpringWebMvcConfig.java +++ b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/autoconfigure/conf/SpringWebMvcConfig.java @@ -19,12 +19,14 @@ import lombok.extern.slf4j.Slf4j; import org.opsli.common.annotation.ApiRestController; import org.opsli.core.api.ApiRequestMappingHandlerMapping; import org.opsli.core.autoconfigure.properties.ApiPathProperties; +import org.opsli.core.filters.interceptor.UserAuthInterceptor; import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; @@ -83,4 +85,10 @@ public class SpringWebMvcConfig implements WebMvcConfigurer, WebMvcRegistrations return new CorsFilter(urlBasedCorsConfigurationSource); } + @Override + public void addInterceptors(InterceptorRegistry registry) { + // 加载特定拦截器 + registry.addInterceptor(new UserAuthInterceptor()); + WebMvcConfigurer.super.addInterceptors(registry); + } } diff --git a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/filters/aspect/TokenAop.java b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/filters/aspect/LogAop.java similarity index 57% rename from opsli-base-support/opsli-core/src/main/java/org/opsli/core/filters/aspect/TokenAop.java rename to opsli-base-support/opsli-core/src/main/java/org/opsli/core/filters/aspect/LogAop.java index 2c286f70..8d773ebd 100644 --- a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/filters/aspect/TokenAop.java +++ b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/filters/aspect/LogAop.java @@ -18,27 +18,18 @@ package org.opsli.core.filters.aspect; import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.TimeInterval; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; -import org.opsli.core.api.TokenThreadLocal; -import org.opsli.common.exception.ServiceException; import org.opsli.core.utils.LogUtil; -import org.opsli.core.utils.UserTokenUtil; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; -import org.springframework.web.context.request.RequestAttributes; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; - -import javax.servlet.http.HttpServletRequest; import static org.opsli.common.constants.OrderConstants.TOKEN_AOP_SORT; /** - * 参数校验 拦截处理 + * 日志 拦截处理 * * @author parker * @date 2020-09-16 @@ -47,7 +38,7 @@ import static org.opsli.common.constants.OrderConstants.TOKEN_AOP_SORT; @Order(TOKEN_AOP_SORT) @Aspect @Component -public class TokenAop { +public class LogAop { @Pointcut("execution(public * org.opsli..*.*Controller*.*(..))") @@ -60,32 +51,6 @@ public class TokenAop { */ @Around("requestMapping()") public Object tokenAop(ProceedingJoinPoint point) throws Throwable { - - // Token - String requestToken = TokenThreadLocal.get(); - - // 如果 ThreadLocal为空 则去当前request中获取 - if(StringUtils.isEmpty(requestToken)){ - // 将 Token放入 线程缓存 - try { - RequestAttributes ra = RequestContextHolder.getRequestAttributes(); - ServletRequestAttributes sra = (ServletRequestAttributes) ra; - if(sra != null) { - HttpServletRequest request = sra.getRequest(); - requestToken = UserTokenUtil.getRequestToken(request); - if(StringUtils.isNotEmpty(requestToken)){ - // 放入当前线程缓存中 - TokenThreadLocal.put(requestToken); - } - } - }catch (ServiceException e){ - throw e; - }catch (Exception e){ - log.error(e.getMessage(),e); - } - } - - // 计时器 TimeInterval timer = DateUtil.timer(); // 执行 @@ -103,13 +68,7 @@ public class TokenAop { long timerCount = timer.interval(); //保存日志 LogUtil.saveLog(point, exception, timerCount); - - // 线程销毁时 删除 token - if(StringUtils.isNotEmpty(requestToken)){ - TokenThreadLocal.remove(); - } } - return returnValue; } diff --git a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/filters/interceptor/UserAuthInterceptor.java b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/filters/interceptor/UserAuthInterceptor.java new file mode 100644 index 00000000..2fcf816e --- /dev/null +++ b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/filters/interceptor/UserAuthInterceptor.java @@ -0,0 +1,61 @@ +/** + * Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.opsli.core.filters.interceptor; + +import lombok.extern.slf4j.Slf4j; +import org.opsli.core.holder.UserContextHolder; +import org.opsli.core.utils.UserTokenUtil; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.resource.ResourceHttpRequestHandler; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * 用户权限拦截器,支持自解析 jwt token + * + * @author 周鹏程 + * @date 2021年12月22日16:35:20 + */ +@Slf4j +public class UserAuthInterceptor implements HandlerInterceptor { + + @Override + public boolean preHandle( + HttpServletRequest request, HttpServletResponse response, Object handler){ + + if (handler instanceof ResourceHttpRequestHandler) { + return true; + } + + try { + String requestToken = UserTokenUtil.getRequestToken(request); + UserContextHolder.setToken(requestToken); + }catch (Exception e){ + log.error(e.getMessage(), e); + } + + return true; + } + + @Override + public void afterCompletion( + HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex){ + // 上下文属性值清除,防止内存泄漏 + UserContextHolder.clear(); + } + +} diff --git a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/general/StartPrint.java b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/general/StartPrint.java index 4e1445a8..e2fb0524 100644 --- a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/general/StartPrint.java +++ b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/general/StartPrint.java @@ -72,13 +72,24 @@ public class StartPrint { * 打印启动日志 */ public void errorPrint(){ + this.errorPrint(null); + } + + /** + * 失败 + * 打印启动日志 + */ + public void errorPrint(String errorMsg){ // 睡一秒打印 ThreadUtil.sleep(1, TimeUnit.SECONDS); String printStr = "\n----------------------------------------------------------\n" + - systemName + " 框架启动失败! 请检查相关配置!\n" + - "----------------------------------------------------------\n"; - Console.log(printStr); + systemName + " 框架启动失败! 请检查相关配置!\n" + + "----------------------------------------------------------\n"; + Console.error(printStr); + if(StringUtils.isNotEmpty(errorMsg)){ + Console.error(errorMsg); + } } diff --git a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/api/TokenThreadLocal.java b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/holder/UserContextHolder.java similarity index 60% rename from opsli-base-support/opsli-core/src/main/java/org/opsli/core/api/TokenThreadLocal.java rename to opsli-base-support/opsli-core/src/main/java/org/opsli/core/holder/UserContextHolder.java index 13700f3c..e584447d 100644 --- a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/api/TokenThreadLocal.java +++ b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/holder/UserContextHolder.java @@ -13,9 +13,9 @@ * License for the specific language governing permissions and limitations under * the License. */ -package org.opsli.core.api; - +package org.opsli.core.holder; +import com.alibaba.ttl.TransmittableThreadLocal; import org.apache.commons.lang3.StringUtils; import org.opsli.core.utils.UserTokenUtil; import org.springframework.web.context.request.RequestAttributes; @@ -23,27 +23,32 @@ import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; +import java.util.Optional; /** - * 用于存放当前线程下 Token + * 用户认证信息上下文 * - * @author parker - * @date 2020-09-15 + * @author 周鹏程 + * @date 2021年12月22日16:22:59 */ -public class TokenThreadLocal { - - /** 临时线程存储 token 容器 */ - private static final ThreadLocal TOKEN_DATA = new ThreadLocal<>(); +public final class UserContextHolder { - public static void put(String token) { - if (TOKEN_DATA.get() == null) { - TOKEN_DATA.set(token); - } - } + /** + * 存储用户上下文信息 + * 采用父子线程共享缓存 + */ + public static final ThreadLocal THREAD_LOCAL = new TransmittableThreadLocal<>(); - public static String get() { + /** + * 获取当前上下文用户 + * 推荐用法,避免空指针: + * LoginUserInfo loginUserInfo = UserContextHolder.get().orElseThrow(() -> new BusinessException("提示未登录即可")); + * + * @return Optional + */ + public static Optional getToken() { - String token = TOKEN_DATA.get(); + String token = THREAD_LOCAL.get(); // 2021-03-10 // 这里纠正 Token 在被多聚合项目 aop切面 remove后 无法获得Token bug @@ -59,12 +64,24 @@ public class TokenThreadLocal { }catch (Exception ignored){} } - return token; + return Optional.ofNullable(token); } - public static void remove() { - try { - TOKEN_DATA.remove(); - }catch (Exception ignored){} + + public static void setToken(String token) { + if (THREAD_LOCAL.get() == null) { + THREAD_LOCAL.set(token); + } } + + public static void clear() { + THREAD_LOCAL.remove(); + } + + + /** + * 私有化构造函数 + */ + private UserContextHolder(){} + } diff --git a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/listener/ApplicationFailedEventListener.java b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/listener/ApplicationFailedEventListener.java index 91929f30..f6c971d8 100644 --- a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/listener/ApplicationFailedEventListener.java +++ b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/listener/ApplicationFailedEventListener.java @@ -18,7 +18,7 @@ public class ApplicationFailedEventListener implements ApplicationListener new TokenException( + TokenMsg.EXCEPTION_TOKEN_LOSE_EFFICACY)); // 1. 校验 token 是否有效 boolean verify = UserTokenUtil.verify(accessToken); @@ -151,7 +155,8 @@ public class JwtRealm extends AuthorizingRealm implements FlagRealm { return; } - String accessToken = TokenThreadLocal.get(); + String accessToken = UserContextHolder.getToken().orElseThrow(() -> new TokenException( + TokenMsg.EXCEPTION_TOKEN_LOSE_EFFICACY)); // 查询 用户信息 String userId = UserTokenUtil.getUserIdByToken(accessToken); diff --git a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/UserTokenUtil.java b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/UserTokenUtil.java index c6a6e865..0ae0f4ab 100644 --- a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/UserTokenUtil.java +++ b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/UserTokenUtil.java @@ -34,9 +34,9 @@ import org.opsli.common.constants.TokenConstants; import org.opsli.common.constants.TokenTypeConstants; import org.opsli.common.enums.LoginLimitRefuse; import org.opsli.common.exception.TokenException; -import org.opsli.core.api.TokenThreadLocal; import org.opsli.core.autoconfigure.properties.GlobalProperties; import org.opsli.core.cache.CacheUtil; +import org.opsli.core.holder.UserContextHolder; import org.opsli.core.msg.CoreMsg; import org.opsli.core.msg.TokenMsg; import org.opsli.plugins.redis.RedisPlugin; @@ -162,7 +162,10 @@ public class UserTokenUtil { * @return String */ public static String getUserIdByToken() { - return getUserIdByToken(TokenThreadLocal.get()); + String token = UserContextHolder.getToken().orElseThrow(() -> new TokenException( + TokenMsg.EXCEPTION_TOKEN_LOSE_EFFICACY)); + + return getUserIdByToken(token); } /** * 根据 Token 获得用户ID @@ -186,7 +189,9 @@ public class UserTokenUtil { * @return String */ public static String getUserNameByToken() { - return getUserNameByToken(TokenThreadLocal.get()); + String token = UserContextHolder.getToken().orElseThrow(() -> new TokenException( + TokenMsg.EXCEPTION_TOKEN_LOSE_EFFICACY)); + return getUserNameByToken(token); } /** * 根据 Token 获得 用户名 @@ -209,7 +214,9 @@ public class UserTokenUtil { * @return String */ public static String getTenantIdByToken() { - return getTenantIdByToken(TokenThreadLocal.get()); + String token = UserContextHolder.getToken().orElseThrow(() -> new TokenException( + TokenMsg.EXCEPTION_TOKEN_LOSE_EFFICACY)); + return getTenantIdByToken(token); } /** diff --git a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/UserUtil.java b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/UserUtil.java index f6a833f1..89e6338f 100644 --- a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/UserUtil.java +++ b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/UserUtil.java @@ -31,10 +31,10 @@ import org.opsli.api.wrapper.system.user.UserModel; import org.opsli.api.wrapper.system.user.UserOrgRefModel; import org.opsli.common.constants.RedisConstants; import org.opsli.common.exception.TokenException; -import org.opsli.core.api.TokenThreadLocal; import org.opsli.core.autoconfigure.properties.GlobalProperties; import org.opsli.core.cache.CacheUtil; import org.opsli.core.cache.SecurityCache; +import org.opsli.core.holder.UserContextHolder; import org.opsli.core.msg.CoreMsg; import org.opsli.core.msg.TokenMsg; import org.springframework.beans.factory.annotation.Autowired; @@ -88,7 +88,8 @@ public class UserUtil { ThrowExceptionUtil.isThrowException(!IS_INIT, CoreMsg.OTHER_EXCEPTION_UTILS_INIT); - String token = TokenThreadLocal.get(); + String token = UserContextHolder.getToken().orElseThrow(() -> new TokenException( + TokenMsg.EXCEPTION_TOKEN_LOSE_EFFICACY)); // 如果还是没获取到token 则抛出异常 if(StringUtils.isEmpty(token)){ @@ -115,7 +116,8 @@ public class UserUtil { ThrowExceptionUtil.isThrowException(!IS_INIT, CoreMsg.OTHER_EXCEPTION_UTILS_INIT); - String token = TokenThreadLocal.get(); + String token = UserContextHolder.getToken().orElseThrow(() -> new TokenException( + TokenMsg.EXCEPTION_TOKEN_LOSE_EFFICACY)); // 如果还是没获取到token 则抛出异常 if(StringUtils.isEmpty(token)){ diff --git a/opsli-modulars/opsli-modulars-system/src/main/java/org/opsli/modulars/system/login/web/LoginRestController.java b/opsli-modulars/opsli-modulars-system/src/main/java/org/opsli/modulars/system/login/web/LoginRestController.java index c149e026..353b7731 100644 --- a/opsli-modulars/opsli-modulars-system/src/main/java/org/opsli/modulars/system/login/web/LoginRestController.java +++ b/opsli-modulars/opsli-modulars-system/src/main/java/org/opsli/modulars/system/login/web/LoginRestController.java @@ -26,17 +26,16 @@ import org.opsli.api.wrapper.system.menu.MenuModel; import org.opsli.api.wrapper.system.options.OptionsModel; import org.opsli.api.wrapper.system.tenant.TenantModel; import org.opsli.api.wrapper.system.user.UserModel; -import org.opsli.common.annotation.LoginCrypto; import org.opsli.common.annotation.Limiter; -import org.opsli.common.enums.DictType; -import org.opsli.common.thread.AsyncProcessExecutor; -import org.opsli.common.thread.AsyncProcessExecutorFactory; -import org.opsli.core.utils.ValidatorUtil; -import org.opsli.core.api.TokenThreadLocal; +import org.opsli.common.annotation.LoginCrypto; import org.opsli.common.enums.AlertType; +import org.opsli.common.enums.DictType; import org.opsli.common.enums.OptionsType; import org.opsli.common.exception.TokenException; +import org.opsli.common.thread.AsyncProcessExecutor; +import org.opsli.common.thread.AsyncProcessExecutorFactory; import org.opsli.common.utils.IPUtil; +import org.opsli.core.holder.UserContextHolder; import org.opsli.core.msg.TokenMsg; import org.opsli.core.utils.*; import org.opsli.modulars.system.login.entity.LoginForm; @@ -53,6 +52,7 @@ import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; import java.util.Map; +import java.util.Optional; /** * 登陆 / 登出 / 验证码 @@ -160,6 +160,9 @@ public class LoginRestController { //生成token,并保存到Redis ResultVo resultVo = UserTokenUtil.createToken(user); if(resultVo.isSuccess()){ + // 保存Token 到当前线程缓存 + UserContextHolder.setToken(resultVo.getData().getToken()); + AsyncProcessExecutor normalExecutor = AsyncProcessExecutorFactory.createNormalExecutor(); // 异步保存IP normalExecutor.put(()->{ @@ -167,6 +170,12 @@ public class LoginRestController { String clientIpAddress = IPUtil.getClientIdBySingle(request); user.setLoginIp(clientIpAddress); iUserService.updateLoginIp(user); + + // TODO + // 记录用户登录日志 如果系统较大 可考虑 Elastic 的 filebeat + // 小系统 直接存在 mysql就好 + + }); normalExecutor.execute(); } @@ -181,7 +190,9 @@ public class LoginRestController { @ApiOperation(value = "登出", notes = "登出") @PostMapping("/system/logout") public ResultVo logout() { - String token = TokenThreadLocal.get(); + String token = UserContextHolder.getToken().orElseThrow(() -> new TokenException( + TokenMsg.EXCEPTION_TOKEN_LOSE_EFFICACY)); + // 登出失败,没有授权Token if(StringUtils.isEmpty(token)){ return ResultVo.error(TokenMsg.EXCEPTION_LOGOUT_ERROR.getMessage()); diff --git a/pom.xml b/pom.xml index 61714768..38494bfb 100644 --- a/pom.xml +++ b/pom.xml @@ -301,6 +301,12 @@ ${fastjson.version} + + com.alibaba + transmittable-thread-local + 2.12.5 + + cn.hutool