From b04450334628c44f4a3c4534141495a29686f35a Mon Sep 17 00:00:00 2001 From: husenlin <husenlinwei@163.com> Date: Thu, 4 Nov 2021 09:40:44 +0800 Subject: [PATCH] =?UTF-8?q?fix:AsyncFactory=E7=B1=BB=E4=B8=AD=E7=9A=84reco?= =?UTF-8?q?rdLogininfor=E6=96=B9=E6=B3=95=E6=8A=9B=E5=87=BA=E7=9A=84NPE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原因:在前端调用退出登录的时候(/logout),SpringSecurity会回调LogoutSuccessHandlerImpl实现类中的onLogoutSuccess方法做一些清理工作,包括记录登录日志。 记录登录日志调用的是AsyncFactory类中的recordLogininfor方法,在此方法中使用ServletUtils.getRequest()方法获取HttpServletRequest 对象。但onLogoutSuccess方法是被【异步】调用的,从而导致ServletUtils.getRequest()返回的一直是null。 证明:虽然因时间原因没具体看SpringSecurity源码,但可以用一个小实验证明。 在JwtAuthenticationTokenFilter类中的doFilterInternal增加一行打印当前线程id的日志;然后 在LogoutSuccessHandlerImpl类中的onLogoutSuccess也相应增加一行打印当前线程id的日志。 最后查看日志发现两个线程id不一致,从而证明onLogoutSuccess方法别【异步】调用。 修复:因为考虑到最好不破坏原API,所以使用ThreadLocal解决该问题。 --- .../framework/manager/factory/AsyncFactory.java | 13 +++++++++++-- .../security/handle/LogoutSuccessHandlerImpl.java | 1 + 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java index f9c4fcca5..b76414623 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java @@ -16,6 +16,8 @@ import com.ruoyi.system.service.ISysLogininforService; import com.ruoyi.system.service.ISysOperLogService; import eu.bitwalker.useragentutils.UserAgent; +import javax.servlet.http.HttpServletRequest; + /** * 异步工厂(产生任务用) * @@ -24,6 +26,7 @@ import eu.bitwalker.useragentutils.UserAgent; public class AsyncFactory { private static final Logger sys_user_logger = LoggerFactory.getLogger("sys-user"); + public static final ThreadLocal<HttpServletRequest> REQUEST_THREAD_LOCAL = ThreadLocal.withInitial(ServletUtils::getRequest); /** * 记录登录信息 @@ -37,8 +40,14 @@ public class AsyncFactory public static TimerTask recordLogininfor(final String username, final String status, final String message, final Object... args) { - final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent")); - final String ip = IpUtils.getIpAddr(ServletUtils.getRequest()); + final HttpServletRequest request = REQUEST_THREAD_LOCAL.get(); + + final String header = request.getHeader("User-Agent"); + final UserAgent userAgent = UserAgent.parseUserAgentString(header); + final String ip = IpUtils.getIpAddr(request); + + REQUEST_THREAD_LOCAL.remove(); + return new TimerTask() { @Override diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java index 1d5b1f9c6..11368a231 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java @@ -46,6 +46,7 @@ public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler // 删除用户缓存记录 tokenService.delLoginUser(loginUser.getToken()); // 记录用户退出日志 + AsyncFactory.REQUEST_THREAD_LOCAL.set(request); AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, "退出成功")); } ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(HttpStatus.SUCCESS, "退出成功")));