<>使用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(); } }