springboot获取当前用户信息的三种方式

 更新时间:2024年10月16日 11:10:40   作者:何中应  
本文详细介绍了在开发中获取当前操作用户信息的三种方式:使用ThreadLocal存储用户信息、通过拦截器和注解结合Shiro框架获取用户信息、以及使用Redis存储用户信息,每种方式都有具体的实现步骤和注意事项,可根据实际需求选择合适的方法

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

方式一:使用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;
    }

本地线程对象,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();
    }
}

需要的时候,直接调用其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 {
}

第二步:创建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);
    }
}

第三步:创建在线用户解析类,其中获取当前用户的信息使用的是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();
    }
}

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

import lombok.Data;

/**
 * 在线用户信息
 */
@Data
public class LoginUser {

    /**
     * 登录人id
     */
    private String id;

    /**
     * 登录人账号
     */
    private String username;

    /**
     * 登录人名字
     */
    private String realname;
}

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

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

方式三:使用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));
    }
     @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;
    }

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);
    }
}

第三步:需要时,根据当前用户的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;
    }

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

总结

以上是三种获取当前用户信息的方式,可以根据实际情况选择;更多相关springboot获取当前用户信息内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 详解Mybatis中万能的Map和模糊查询写法

    详解Mybatis中万能的Map和模糊查询写法

    这篇文章主要介绍了Mybatis中万能的Map和模糊查询写法的相关资料,帮助大家更好的理解和使用Mybatis,感兴趣的朋友可以了解下
    2021-03-03
  • Mybatis的核心配置文件使用方法

    Mybatis的核心配置文件使用方法

    Mybatis的核心配置文件有两个,一个是全局配置文件,它包含了会深深影响Mybatis行为的设置和属性信息;一个是映射文件,它很简单,让用户能更专注于SQL代码,本文主要介绍了Mybatis的核心配置文件使用方法,感兴趣的可以了解一下
    2023-11-11
  • Mybatis配置解析看这一篇就够了

    Mybatis配置解析看这一篇就够了

    这篇文章主要介绍了详解Mybatis是如何解析配置文件的,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-07-07
  • 通俗易懂学习java并发工具类-Semaphore,Exchanger

    通俗易懂学习java并发工具类-Semaphore,Exchanger

    这篇文章主要介绍了java并发工具类-Semaphore,Exchanger,java并发工具类有很多,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,下面小编带大家来一起学习一下吧
    2019-06-06
  • Java创建线程的七种方法总结(全网最全面)

    Java创建线程的七种方法总结(全网最全面)

    线程是Java中的基本执行单元,它允许程序在同一时间执行多个任务,下面这篇文章主要给大家总结介绍了关于Java创建线程的七种方法,文中通过实例代码将这七种方法介绍的非常详细,需要的朋友可以参考下
    2023-05-05
  • Maven统一版本管理的实现

    Maven统一版本管理的实现

    在使用Maven多模块结构工程时,配置版本是一个比较头疼的事,本文主要介绍了Maven统一版本管理的实现,具有一定的参考价值,感兴趣的可以了解一下
    2024-03-03
  • SpringBoot整合Mybatis-plus的具体过程使用

    SpringBoot整合Mybatis-plus的具体过程使用

    这篇文章主要介绍了SpringBoot 整合mybatis+mybatis-plus的步骤,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-06-06
  • spring中时间格式化的两种方法示例讲解

    spring中时间格式化的两种方法示例讲解

    这篇文章主要介绍了spring中时间格式化的两种方法,方法一自己格式化,方法二通过配置,结合实例代码讲解的非常详细,文中补充介绍了Spring项目中时间格式化的方法,需要的朋友可以参考下
    2023-08-08
  • java创建以任意图片为背景的窗口

    java创建以任意图片为背景的窗口

    这篇文章主要为大家详细介绍了java创建以任意图片为背景的窗口,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-12-12
  • mybatis之foreach用法详解

    mybatis之foreach用法详解

    这篇文章主要介绍了mybatis之foreach用法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-12-12

最新评论