使用ThreadLocal替代Session完成保存用户登录信息功能
使用ThreadLocal替代Session的好处:
- 可以在同一线程中很方便的获取用户信息,不需要频繁的传递session对象。
具体实现流程:
- 在登录业务代码中,当用户登录成功时,生成一个登录凭证存储到redis中,将凭证中的字符串保存在cookie中返回给客户端。
- 使用一个拦截器拦截请求,从cookie中获取凭证字符串与redis中的凭证进行匹配,获取用户信息,将用户信息存储到ThreadLocal中,在本次请求中持有用户信息,即可在后续操作中使用到用户信息。
登录凭证类:
//登录凭证表
@Data
@ApiModel("登录凭证类")
public class LoginTicket {
private int id;
private int userId;
//登录凭证字符串
private String ticket;
private int status;
private Date expired;
}
ThreadLocal类:
-
ThreadLocal本质是以线程为key存储元素
-
ThreadLocal可以把用户信息保存在线程中,用户的每一次请求,就是一个线程,保存了用户信息,方便我们在后续操作获取用户登录信息。
-
当请求结束,我们会把保存的用户信息清除掉,防止内存泄漏。
/**
*持有用户信息,用于代替session对象
*/
@Component
public class HostHolder {
//ThreadLocal本质是以线程为key存储元素
private ThreadLocal<User> users = new ThreadLocal<>();
public void setUser(User user){
users.set(user);
}
public User getUser(){
return users.get();
}
public void clear(){
users.remove();
}
}
拦截器:
- 拦截每一次请求,从cookie中获取凭证字符串与redis中的凭证进行匹配,获取用户信息,将用户信息存储到ThreadLocal中,在本次请求中持有用户信息,即可在后续操作中使用到用户信息
@Component
public class LoginTicketInterceptor implements HandlerInterceptor {
@Autowired
private UserService userService;
@Autowired
private HostHolder hostHolder;
// 保存登录信息
// 调用时间:Controller方法处理之前
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//从cookie中获取凭证
String ticket = CookieUtil.getValue(request, "ticket");
if (ticket != null){
//查询凭证
LoginTicket loginTicket = userService.findLoginTicket(ticket);
//检查凭证是否有效
if (loginTicket != null && loginTicket.getStatus() == 0 && loginTicket.getExpired().after(new Date())){
//根据凭证查询用户
User user = userService.findByUserId(loginTicket.getUserId());
//在本次请求中持有用户
hostHolder.setUser(user);
//构建用户认证的结果,并存入SecurityContext,以便于Security进行授权
Authentication authentication = new UsernamePasswordAuthenticationToken(
user, user.getPassword(), userService.getAuthorities(user.getId()));
SecurityContextHolder.setContext(new SecurityContextImpl(authentication));
}
}
return true;
}
// 调用时间:Controller方法处理完之后
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
//得到当前线程持有的user
User user = hostHolder.getUser();
if (user != null && modelAndView != null){
modelAndView.addObject("loginUser", user);
}
}
// 调用时间:DispatcherServlet进行视图的渲染之后
// 请求结束,把保存的用户信息清除掉
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
hostHolder.clear();
SecurityContextHolder.clearContext();
}
}