ThreadLocal获取用户信息

系列文章目录

文章目录

  • 系列文章目录
  • 前言
  • 1. ThreadLocal在web项目中的使用姿势
  • 2. 代码实现
    • 2.1 编写ThreadLocal工具类
    • 2.2 编写拦截器


前言

在之前的政企系统中,我们随时都可能需要获取当前操作的用户信息。针对这个需求,我们使用ThreadLocal 来解决。若使用了SpringSecurity,则SpringSecurity中提供了对应的解决方案(大概也是用ThreadLocal 实现的)。

此模块中演示ThreadLocal web项目中的使用姿势。

将token解析后,得到对应的用户信息User对象。把User对象存入ThreadLocal中,
那么我们就可以在一次请求的生命周期中(一次线程声明周期中)可以很方便的拿到这个User对象。
然后可以做成一个工具类如:UserUtils.getCurrentUser()来使用。


1. ThreadLocal在web项目中的使用姿势

由于程序是运⾏在web容器中,每⼀个HTTP请求都是⼀个独⽴线程,也就是可以理解成我们编写的应⽤程序运⾏在⼀个多线程的环境中,那么我们就可以使⽤ThreadLocal在HTTP请求的⽣命周期内进⾏存值、取值操作。

我们想要知道 执行当前请求的用户信息,如何知道呢?
即:我们可以在拦截器中解析token获取到用户的信息User对象,存入到ThreadLocal 对象中,那么我们就可以随时随地的获取到 执行当前请求的用户信息User对象了。如下图:我们可以随时随地的获取到当前用户的信息。ThreadLocal获取用户信息_第1张图片
我们对上图进行一个说明:

  • ⽤户的每⼀个请求,都是⼀个独⽴的线程。
  • 图中的TL就是ThreadLocal,⼀旦将数据绑定到ThreadLocal中,那么在整个请求的⽣命周期内都可以随时拿到ThreadLocal中当前线程的数据。

根据上⾯的分析,我们只需要在Controller请求之前 进⾏对token做校验,如果token有效,则会拿到User对象,然后将该User对象保存到ThreadLocal中即可,最后放⾏请求,在后续的各个环节中都可以获取到该数据了。

如果token⽆效,给客户端响应401状态码,拦截请求,不再放⾏到Controller中。由此可⻅,这个校验的逻辑是⽐较适合放在拦截器中完成的。


2. 代码实现

2.1 编写ThreadLocal工具类

public class UserUtils {
    private static final ThreadLocal<User> LOCAL = new ThreadLocal<>();

    private UserThreadLocal() {
    }

    /**
     * 将对象放⼊到ThreadLocal
     *
     * @param user
     */
    public static void set(User user) {
        LOCAL.set(user);
    }

    /**
     * 返回当前线程中的User对象
     *
     * @return
     */
    public static User get() {
        return LOCAL.get();
    }

    /**
     * 删除当前线程中的User对象
     */
    public static void remove() {
        LOCAL.remove();
    }
}

2.2 编写拦截器

编写拦截器:

@Component
public class UserTokenInterceptor implements HandlerInterceptor {

    @Autowired
    private UserService userService;

    /**
     * 请求前拦截
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse
            response, Object handler) throws Exception {
            
        /*//校验handler是否是HandlerMethod
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        //判断是否包含@NoAuthorization注解,如果包含,直接放⾏
        if (((HandlerMethod)
                handler).hasMethodAnnotation(NoAuthorization.class)) {
            return true;
        }*/
        
        //从请求头中获取token
        String token = request.getHeader("Authorization");
        if (!StringUtils.isEmpty(token)) {
        	// 解析Token,并查库获取User对象
            User user = this.userService.queryUserByToken(token);
            if (user != null) {
                //token有效
                //将User对象放⼊到ThreadLocal中
                UserUtils.set(user);
                return true;
            }
        }
        //token⽆效,响应状态为401
        response.setStatus(401); //⽆权限
        return false;
    }

    /**
     * 清理资源
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse
            response, Object handler, Exception ex) throws Exception {
        //从ThreadLocal中移除User对象
        UserThreadLocal.remove();
    }
}

注册拦截器:WebConfig

@Configuration
public class WebConfig implements WebMvcConfigurer {
 
    //@Autowired
    //private RedisCacheInterceptor redisCacheInterceptor;
    @Autowired
    private UserTokenInterceptor userTokenInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //需要考虑拦截器的顺序
        registry.addInterceptor(this.userTokenInterceptor).addPathPatterns("/**");

       //registry.addInterceptor(this.redisCacheInterceptor).addPathPatterns("/**");
    }
}

然后在需要使用当前用户信息的地方直接:

User currentUser = UserUtils.get();

你可能感兴趣的:(安全-认证授权,项目开发必知知识点)