• 获取当前用户信息的几种方式


    说明:在开发中,我们经常需要获取当前操作的用户信息,如创建用户、创建订单时,我们需要记录下创建人,本文介绍获取当前用户信息的三种方式。

    方式一:使用ThreadLocal

    ThreadLocal本质上是一个Map,键是当前线程,值是存入的信息。我们可以在用户登录,校验用户信息后,将所需要的用户信息存入到ThreadLocal中,如用户ID、用户Token等,然后在需要的时候直接使用即可。

    如下,在preHandle()方法中,将当前用户的ID存入到TokenThreadLocal对象中,

    @Component
    public class TokenInterceptor implements HandlerInterceptor {
    
        @Value("${token.key}")
        private String tokenKey;
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        
            // 获取请求头 拿到Token
            String token = request.getHeader("Token");
            String authToken = request.getHeader("authentication");
    
            if (StrUtil.isAllEmpty(token,authToken)){
               return responseHandler(response);
            }
    
            //  校验Token的合法性和有效性
            JWT jwt = JWTUtil.parseToken(StrUtil.isBlank(token) ? authToken : token);
    
            try {
                // 校验Token是否合法 校验Token是否过期
                if (!(jwt.setKey(tokenKey.getBytes()).verify() && jwt.validate(0))){
                    return responseHandler(response);
                }
            } catch (Exception e) {
                // 抛出自定义异常 Token是非法的
                // throw new RuntimeException(e);
                return responseHandler(response);
            }
    
            // 把Token的信息解析出来放到ThreadLocal
            Long id = Convert.toLong(jwt.getPayload("id"));
    
            // 设置本地线程池中的用户ID
            TokenThreadLocal.set(id);
            
            // 放行
            return true;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    本地线程对象,TokenThreadLocal

    /**
     * 本地线程对象
     *
     * 存放用户ID
     */
    public class TokenThreadLocal {
    
        /**
         * 创建一个ThreadLocal对象
         */
        private static final ThreadLocal<Long> THREAD_LOCAL= new ThreadLocal<>();
    
        /**
         * 添加一个数据
         * @param key
         */
        public static void set(Long key){
            THREAD_LOCAL.set(key);
        }
    
        /**
         * 获取一个数据
         * @return
         */
        public static Long get(){
            return THREAD_LOCAL.get();
        }
    
        /**
         * 删除一个数据
         */
        public static void remove(){
            THREAD_LOCAL.remove();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    需要的时候,直接调用其get()方法,下面是使用AOP+自定义注解实现对创建、更新操作字段的填充;

    在这里插入图片描述

    注意,需要在afterCompletion()方法中调用ThreadLocal的remove()方法,避免内存泄漏;

    在这里插入图片描述

    方式二:通过拦截器和相应注解实现

    如果项目中,登录校验框架使用的是Shiro,有一种更方便的方式,如下:

    第一步:创建一个自定义注解,如LoginInfo,表示登录用户的信息,注意元注解的属性;

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * 登录用户
     * @author 
     */
    @Target(value = ElementType.PARAMETER)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface LoginInfo {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    第二步:创建MVC的配置类,注入一个在线用户解析对象,后面实现;

    import org.decent.modules.integral.resolver.LoginUserArgumentResolver;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.method.support.HandlerMethodArgumentResolver;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    import javax.annotation.Resource;
    import java.util.List;
    
    /**
     * web设置
     * @author
     */
    @Configuration
    public class WebConfig implements WebMvcConfigurer {
    
        @Resource
        private LoginUserArgumentResolver loginUserArgumentResolver;
    
        @Override
        public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
            resolvers.add(loginUserArgumentResolver);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    第三步:创建在线用户解析类,其中获取当前用户的信息使用的是Shiro框架的方法,SecurityUtils.getSubject().getPrincipal(),该方法返回的是一个Object类型的对象;

    import org.apache.shiro.SecurityUtils;
    import org.decent.modules.integral.annotation.LoginInfo;
    import org.springframework.core.MethodParameter;
    import org.springframework.stereotype.Component;
    import org.springframework.web.bind.support.WebDataBinderFactory;
    import org.springframework.web.context.request.NativeWebRequest;
    import org.springframework.web.method.support.HandlerMethodArgumentResolver;
    import org.springframework.web.method.support.ModelAndViewContainer;
    
    /**
     * 登录解析实现
     *
     * @author
     */
    @Component
    public class LoginUserArgumentResolver implements HandlerMethodArgumentResolver {
        @Override
        public boolean supportsParameter(MethodParameter methodParameter) {
            return methodParameter.hasParameterAnnotation(LoginInfo.class);
        }
    
        @Override
        public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
            return SecurityUtils.getSubject().getPrincipal();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    第四步:创建一个在线用户的JavaBean,存放一些可能会用得上的属性;

    import lombok.Data;
    
    /**
     * 在线用户信息
     */
    @Data
    public class LoginUser {
    
        /**
         * 登录人id
         */
        private String id;
    
        /**
         * 登录人账号
         */
        private String username;
    
        /**
         * 登录人名字
         */
        private String realname;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    第五步:在需要使用的接口上,直接使用注解即可。当请求访问该接口时,会被前面的拦截器拦截住,然后把当前用户的信息取出来并封装到JavaBean对象中,非常方便;

    	@PostMapping(value = "/add")
    	public Result<?> add(@LoginInfo LoginUser loginUser) {
            ......
    	}
    
    • 1
    • 2
    • 3
    • 4

    方式三:使用Redis存储用户信息

    这种方式思路和第一种相同,当用户通过校验时,将用户信息查询出来并存起来,需要的时候再取出来用。当然,使用Redis存储比ThreadLocal更灵活一点,可以设置有效时间。实现如下:

    第一步:登录验证通过,将用户信息存入Redis;

        @PostMapping("/login")
        public Result<?> counterLogin(@RequestBody LoginBody LoginUser){
            // 登录
            LoginUser userInfo = sysLoginService.login(LoginUser.getUsername(), LoginUser.getPassword(),LoginUser.getCounterType());
            
            // 创建Token并返回
            return Result.success(tokenService.createToken(userInfo));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
         @Autowired
        private RedisService redisService;
    
    	// 定义有效时间,为720 * 60 秒,即12小时
        private final static long EXPIRE_TIME = 720 * 60;
    
    	/**
         * 创建令牌
         */
        public Map<String, Object> createToken(LoginUser loginUser){
            // 生成token
            String token = IdUtils.fastUUID();
            loginUser.setToken(token);
            loginUser.setUserid(loginUser.getSysUser().getUserId());
            loginUser.setUsername(loginUser.getSysUser().getUserName());
    
            // 保存用户token
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("token", token);
            map.put("loginUser",loginUser);
    
    		// 将该用户的信息存入到Redis中
            redisService.setCacheObject(token, loginUser, EXPIRE_TIME, TimeUnit.SECONDS);
            
            return map;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    RedisService类相关方法

    /**
     * spring redis 工具类
     **/
    @Component
    public class RedisService{
    
        @Autowired
        public RedisTemplate redisTemplate;
        
        /**
         * 缓存基本的对象
         */
        public <T> void setCacheObject(final String key, final T value, final Long timeout, final TimeUnit timeUnit){
            redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
        }
    
        /**
         * 获得缓存的基本对象
         */
        public <T> T getCacheObject(final String key){
            ValueOperations<String, T> operation = redisTemplate.opsForValue();
            return operation.get(key);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    第三步:需要时,根据当前用户的Token,去Redis中取出该用户的信息;

        /**
         * 根据用户Token获取用户身份信息
         *
         * @return 用户信息
         */
        public LoginUser getLoginUser(String token){
            if (StringUtils.isNotEmpty(token)){
                String userKey = getTokenKey(token);
                LoginUser user = redisService.getCacheObject(userKey);
                return user;
            }
            return null;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    用户的Token是需要放在Request对象里面的,所以可以再写一个TokenService对象,用来获取当前用户的Token,并调用RedisService获取当前用户信息,进行进一步的封装。

    总结

    以上是三种获取当前用户信息的方式,可以根据实际情况选择;

  • 相关阅读:
    云原生微服务开发日趋成熟:有效拥抱左移以改善交付
    29、Elasticsearch进阶查询
    非零基础自学Java (老师:韩顺平) 第4章 运算符 4.21 二进制转八进制等 && 4.27 原码、反码、补码 && 4.28 位运算符
    GFS分布式文件系统
    模数转换器-ADC基础
    Django第三章(模版系统全局变量-if判断-for循环-过滤器-模版继承/引用-引用静态文件)
    力扣labuladong——一刷day01
    计算机毕业设计(附源码)python在线党建学习平台
    打造南沙“强芯”,南沙首届IC Nansha大会召开
    服务器数据备份最佳实践快速指南
  • 原文地址:https://blog.csdn.net/qq_42108331/article/details/134543953