fix:AsyncFactory类中的recordLogininfor方法抛出的NPE

原因:在前端调用退出登录的时候(/logout),SpringSecurity会回调LogoutSuccessHandlerImpl实现类中的onLogoutSuccess方法做一些清理工作,包括记录登录日志。
记录登录日志调用的是AsyncFactory类中的recordLogininfor方法,在此方法中使用ServletUtils.getRequest()方法获取HttpServletRequest
对象。但onLogoutSuccess方法是被【异步】调用的,从而导致ServletUtils.getRequest()返回的一直是null。

证明:虽然因时间原因没具体看SpringSecurity源码,但可以用一个小实验证明。
在JwtAuthenticationTokenFilter类中的doFilterInternal增加一行打印当前线程id的日志;然后
在LogoutSuccessHandlerImpl类中的onLogoutSuccess也相应增加一行打印当前线程id的日志。
最后查看日志发现两个线程id不一致,从而证明onLogoutSuccess方法别【异步】调用。

修复:因为考虑到最好不破坏原API,所以使用ThreadLocal解决该问题。
This commit is contained in:
husenlin 2021-11-04 09:40:44 +08:00
parent bbbe83b737
commit b044503346
2 changed files with 12 additions and 2 deletions

View File

@ -16,6 +16,8 @@ import com.ruoyi.system.service.ISysLogininforService;
import com.ruoyi.system.service.ISysOperLogService; import com.ruoyi.system.service.ISysOperLogService;
import eu.bitwalker.useragentutils.UserAgent; import eu.bitwalker.useragentutils.UserAgent;
import javax.servlet.http.HttpServletRequest;
/** /**
* 异步工厂产生任务用 * 异步工厂产生任务用
* *
@ -24,6 +26,7 @@ import eu.bitwalker.useragentutils.UserAgent;
public class AsyncFactory public class AsyncFactory
{ {
private static final Logger sys_user_logger = LoggerFactory.getLogger("sys-user"); 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, public static TimerTask recordLogininfor(final String username, final String status, final String message,
final Object... args) final Object... args)
{ {
final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent")); final HttpServletRequest request = REQUEST_THREAD_LOCAL.get();
final String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
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() return new TimerTask()
{ {
@Override @Override

View File

@ -46,6 +46,7 @@ public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler
// 删除用户缓存记录 // 删除用户缓存记录
tokenService.delLoginUser(loginUser.getToken()); tokenService.delLoginUser(loginUser.getToken());
// 记录用户退出日志 // 记录用户退出日志
AsyncFactory.REQUEST_THREAD_LOCAL.set(request);
AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, "退出成功")); AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, "退出成功"));
} }
ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(HttpStatus.SUCCESS, "退出成功"))); ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(HttpStatus.SUCCESS, "退出成功")));