JAVA简单封装UserUtil

目录

思路

一、TokenFilterConfiguration

二、FilterConfig

三、TokenContextHolder

四、TokenUtil

五、UserUtil


思路

  1. 配置Token过滤器(TokenFilterConfiguration):实现一个Token过滤器配置,用于拦截HTTP请求,从请求头中提取Token,并通过调用TokenUtil验证Token的有效性

    1. 无效:response响应401

    2. 有效:将Token值存储在ThreadLocal中

  2. 配置过滤器(FilterConfig):用于注册和统一管理所有自定义过滤器

  3. 配置Holder类(TokenContextHolder):利用ThreadLocal机制管理每个线程绑定的Token,确保在一个线程内的请求间Token的一致性和隔离性。

  4. 创建TokenUtil工具类:token为key获取redis中数据,调用TokenUtil中的方法调feign获取用户信息,验证token是否有效

  5. 创建UserUtil工具类:从TokenContextHolder中获取当前的token,用TokenUtil解析token,获取用户信息

一、TokenFilterConfiguration

Token隔离过滤器配置类,实现基于Bearer Token的权限验证

/**
 * Token隔离过滤器配置类,用于实现基于Bearer Token的权限验证。
 * 该类作为一个过滤器,拦截进入系统的HTTP请求,验证请求中携带的Token是否有效,
 * 从而决定是否允许请求继续传递到系统内部。
 */
@Slf4j
@Configuration
public class TokenFilterConfiguration implements Filter {

    /**
     * 处理HTTP请求。
     * 验证请求中是否包含有效的Bearer Token,并在验证通过后将Token值存储在ThreadLocal中,
     * 以便后续的业务逻辑可以获取到当前请求的用户信息。
     *
     * @param servletRequest    HTTP请求对象
     * @param servletResponse   HTTP响应对象
     * @param filterChain       过滤器链,用于继续处理或终止请求的传递
     * @throws IOException      如果在处理请求或响应时发生I/O错误
     * @throws ServletException 如果在处理请求或响应时发生Servlet相关错误
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        try {
            // 从请求头中提取Bearer Token
            // 检查是否已经登录
            String tokenValue = BearerTokenExtractorUtil.extract(request);
            log.info("过滤器获取到token:{}", tokenValue);

            // 如果Token存在,则验证Token的有效性
            if (StrUtil.isNotEmpty(tokenValue)) {
                // 验证是否有效
                SysUserDto tokenUser = getUserInfoFromSys(tokenValue);
                // 如果Token无效,则返回401未授权错误
                if (ObjectUtil.isEmpty(tokenUser)) {
                    // 如果token无效,直接响应401给客户端并结束执行
                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                    response.getWriter().write("Unauthorized: token失效,未获取到用户登录信息,请重新登录");
                    return;
                }

                // Token有效,将Token值存储在ThreadLocal中,以便后续使用
                // 有效登录的情况,把请求头中的token设置进上下文
                TokenContextHolder.setToken(tokenValue);
                log.info("将请求头中的token设置进上下文context");
            }
        } catch (Exception e) {
            // 记录Token验证过程中的异常
            // 统一异常处理
            log.error("获取用户信息异常:", e);
            // 抛出未授权异常,提示客户端重新登录
            throw new UnauthorizedException("登录状态异常,请重新登录");
        }
        
        // 所有前置检查通过后,继续执行过滤器链
        filterChain.doFilter(servletRequest, servletResponse);

        // 请求处理完成后,清除ThreadLocal中的Token值
        TokenContextHolder.clear();
    }
}

二、FilterConfig

注册和管理过滤器

/**
 * 配置类,用于注册和管理过滤器。
 */
@Configuration
public class FilterConfig {

    /**
     * 注册认证过滤器。
     * 创建一个FilterRegistrationBean实例,用于配置和注册自定义的TokenFilter。
     * 通过设置过滤器适用的URL模式,确保只有指定的API请求才会经过该过滤器进行认证检查。
     *
     * @return FilterRegistrationBean 对象,用于注册过滤器并配置其属性。
     */
    @Bean
    public FilterRegistrationBean authenticationFilterRegistration() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new TokenFilterConfiguration());
        registration.addUrlPatterns("/api/*","/flow/*");
        return registration;
    }
}

三、TokenContextHolder

管理线程绑定的token,提供线程安全的访问方式

/**
 * TokenContextHolder 类用于管理线程绑定的token,提供线程安全的访问方式。
 * 利用ThreadLocal实现线程之间的隔离,确保每个线程操作的token互不干扰。
 */
public class TokenContextHolder {

    /**
     * 使用TransmittableThreadLocal来存储线程绑定的token。
     * TransmittableThreadLocal相比于普通的ThreadLocal,还支持在子线程中传递值,
     * 适用于需要跨线程传递上下文信息的场景。
     */
    /**
     * 支持父子线程之间的数据传递
     */
    private static final ThreadLocal<String> CONTEXT = new TransmittableThreadLocal<>();

    /**
     * 设置当前线程的token。
     * 
     * @param token 要设置的token值。
     */
    public static void setToken(String token) {
        CONTEXT.set(token);
    }

    /**
     * 获取当前线程的token。
     * 
     * @return 当前线程绑定的token值。
     */
    public static String getToken() {
        return CONTEXT.get();
    }

    /**
     * 清除当前线程的token。
     * 该方法用于在不再需要token时,或者在处理完特定业务逻辑后,清除线程绑定的token,以避免潜在的安全问题。
     */
    public static void clear() {
        CONTEXT.remove();
    }
}

四、TokenUtil

token工具类


@Slf4j
@Component
public class TokenUtil implements BeanFactoryAware {
    @Setter
    private static RedisTemplate<String, Object> redisTemplate;
    @Setter
    private static SysFlowableFeign sysFlowableFeign;

    private static final String FLOWABLE_TOKEN_KEY = "token_user:";

     /**
     * 设置BeanFactory,用于获取RedisTemplate和SysFlowableFeign实例。
     */
    @Override
    @SuppressWarnings("unchecked")
    public void setBeanFactory(@NonNull BeanFactory beanFactory) throws BeansException {
        RedisTemplate<String, Object> redisBean = beanFactory.getBean("redisTemplate", RedisTemplate.class);
        setRedisTemplate(redisBean);
        SysFlowableFeign sysFlowableFeignBean = beanFactory.getBean(SysFlowableFeign.class);
        setSysFlowableFeign(sysFlowableFeignBean);
    }

     /**
     * 构建Token的键值。
     * 
     * @param token 用户的Token字符串。
     * @return 构建后的Token键值。
     */
    private static String getTokenKey(String token) {
        return  TOKEN_KEY + token;
    }

    /**
     * 根据Token获取用户信息。
     * 
     * @param token 用户的Token字符串。
     * @return 用户信息的SysUserDto对象。
     * @throws UnauthorizedException 如果Token无效或过期,则抛出此异常。
     */
    public static SysUserDto getTokenUser(String token) {
        try {
            String tokenKey = getTokenKey(token);

            // 缓存中获取
            SysUserDto redisUser = (SysUserDto) redisTemplate.opsForValue().get(tokenKey);

            if (ObjectUtil.isNotNull(redisUser)) {
                log.info("根据token获取缓存中的用户信息:{}", JSONUtil.toJsonStr(redisUser));
                return redisUser;
            }
            log.info("缓存中的用户信息已过期");

            // 缓存中未找到用户信息,从SYS验证token是否已过期
            SysUserDto userInfoFromSys = getUserInfoFromSys(token);
            if (ObjectUtil.isNull(userInfoFromSys)) {
                log.info("缓存中未找到用户信息,从SYS验证token已过期");
                // 未获取到用户信息
                throw new UnauthorizedException("Unauthorized: token失效,未获取到用户登录信息,请重新登录");
            }
            log.info("缓存中未找到用户信息,根据token获取SYS用户信息:{}", JSONUtil.toJsonStr(userInfoFromSys));
            // 存储到本地缓存
            redisTemplate.opsForValue().set(tokenKey, userInfoFromSys);
            // 设置缓存时间1天有效
            redisTemplate.expire(tokenKey, 1, TimeUnit.DAYS);
            return userInfoFromSys;

        } catch (Exception e) {
            // 统一异常处理
            log.error("获取用户信息异常:", e);
            throw new UnauthorizedException("登录状态异常,请重新登录");
        }
    }

    /**
    * feign获取用户信息
    * @param token 用户的Token字符串。
    * @return 用户信息的SysUserDto对象,如果获取失败则返回null。
    */
    public static SysUserDto getUserInfoFromSys(String token) {
        ApiResult<SysUserDto> tokenUserResult = sysFlowableFeign.getTokenUser(token);
        if (BooleanUtil.isTrue(tokenUserResult.getSuccess())) {
            return tokenUserResult.getData();
        }
        return null;
    }

}

五、UserUtil

用户工具类

/**
 * 用户工具类,提供获取当前登录用户的业务逻辑。
 */
public class UserUtil {

    /**
     * 日志记录器,用于记录类的操作日志。
     */
    private static final Logger log = LoggerFactory.getLogger(UserUtil.class);

    // 私有构造方法,防止外部实例化
    private UserUtil() {
    }

    /**
     * 获取当前登录的用户信息。
     *
     * @return 当前登录用户的SysUserDto对象。
     * @throws UnauthorizedException 如果token失效或未获取到用户登录信息,抛出此异常。
     * @throws IllegalArgumentException 如果未从上下文中获取到token,抛出此异常。
     */
    public static SysUserDto getCurrentUser() {
        // 从TokenContextHolder中获取当前的token
        String token = TokenContextHolder.getToken();
        log.info("获取上下文context中的token:{}", token);
 
        if (CharSequenceUtil.isNotEmpty(token)) {
            // 使用TokenUtil解析token,获取用户信息
            SysUserDto tokenUser = TokenUtil.getTokenUser(token);
 
            if (ObjectUtil.isEmpty(tokenUser)) {
                throw new UnauthorizedException("Unauthorized: token失效,未获取到用户登录信息,请重新登录");
            }

            // 返回解析得到的用户信息
            return tokenUser;
        }
 
        throw new IllegalArgumentException("未获取到用户信息");
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伏颜.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值