通过拦截获取登录用户信息的实现

一、引言

在系统运行中,获取登录用户信息是常见需求,但此操作常与业务代码无关,且在一次请求链路中可能多次使用。因此,采用拦截器结合 ThreadLocal 的方式来优化处理,其逻辑如下:于请求执行前,通过拦截器拦截请求,依据请求中的 tokenuserId 获取用户信息并存入 ThreadLocal;在请求执行后,再次通过拦截器拦截请求,清除 ThreadLocal 中对应请求的用户信息。如此,实现了获取登录用户信息与业务代码的解耦,以及单次请求中用户信息的复用。

二、代码逻辑

1、依赖引入

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>transmittable-thread-local</artifactId>
    <version>2.12.6</version>
</dependency>

引入了 com.alibabatransmittable-thread-local 库,用于处理线程本地变量的传递。

2、登录用户信息类 LoginUserInfoContext

package com.fhey.model.bo;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class LoginUserInfoContext {
    private String userId;
    private String loginName;
    private String userName;
    private Integer department;
    private String token;
}

该类定义了登录用户的丰富信息字段,涵盖用户 ID、登录名、用户名、所属部门和令牌等。借助 Lombok 注解,自动生成了相关方法,简化了代码编写。

3、登录用户信息管理类 LoginUserInfoManager

package com.fhey.manager.login;

import com.alibaba.ttl.TransmittableThreadLocal;
import com.fhey.model.bo.LoginUserInfoContext;

/**
 * @author: fhey
 * @create: 2024-08-08 15:07
 * @description: 登录用户信息管理
 */
public class LoginUserInfoManager {
    public static final ThreadLocal<LoginUserInfoContext> USER_INFO_HOLDER = new TransmittableThreadLocal<>();

    public static LoginUserInfoContext get() {
        return USER_INFO_HOLDER.get();
    }

    public static void set(LoginUserInfoContext loginUserInfoContext) {
        USER_INFO_HOLDER.set(loginUserInfoContext);
    }

    public static void remove() {
        USER_INFO_HOLDER.remove();
    }
}

LoginUserInfoManager 类借助 TransmittableThreadLocal 存储登录用户信息,并提供了获取、设置和移除用户信息的方法,有效避免了内存泄漏。

4、登录用户信息拦截器 AuthenticationInterceptor

package com.fhey.interceptor;

import com.fhey.exception.SystemException;
import com.fhey.manager.login.LoginUserInfoManager;
import com.fhey.model.bo.LoginUserInfoContext;
import com.fhey.model.po.User;
import com.fhey.service.user.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


/**
 * @author: fhey
 * @create: 2024-08-08 15:23
 * @description: 获取登录用户信息拦截器
 */
@Slf4j
@Component
public class AuthenticationInterceptor implements HandlerInterceptor {
    private static final String USER_ID = "userId";

    @Autowired
    private UserService userService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object obj) {

        // 先不校验权限数据 如果不是映射到方法直接通过
        if (!(obj instanceof HandlerMethod)) {
            return true;
        }
        
        String userId = request.getHeader(USER_ID);
        // userId校验
        if (StringUtils.isEmpty(userId)) {
            return true;
        }
        try {
            // 从 http 请求头中取出 userId,因为系统在网关层就验证过token,并解析token获取userId并放入请求头里,所以这里直接从请求头中获取。如果不是这样的逻辑则建议换成一个根据toeken获取用户信息的方法
            LoginUserInfoContext userInfo = getUserInfo(userId);
            if(userInfo == null){
                throw new NullPointerException();
            }
            LoginUserInfoManager.set(userInfo);
        } catch (NullPointerException e) {
            LoginUserInfoManager.remove();
            throw new SystemException("解析用户信息失败");
        }
        return true;
    }


    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object obj, ModelAndView modelAndView) {
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object obj, Exception e) {
        //移除线程变量, 防止内存泄漏
        LoginUserInfoManager.remove();
    }

    //根据系统自己实现,此处仅为示例
    LoginUserInfoContext getUserInfo(String userId) {
        User user = userService.getById(userId);
        if(user == null){
            return null;
        }
        return LoginUserInfoContext.builder()
              .userId(userId)
              .userName(user.getUserName())
              .department(user.getDepartment()).build();
    }
}

AuthenticationInterceptor 类实现了 HandlerInterceptor 接口,在请求处理前获取登录用户信息。若获取失败,会抛出异常并移除已设置的用户信息,请求处理结束后也会移除线程变量以防内存泄漏。

5、拦截器配置类 InterceptorConfig

package com.fhey.config;

import com.fhey.interceptor.AuthenticationInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

/**
 * @author: fhey
 * @create: 2024-08-08 15:24
 * @description: 拦截器配置
 */
@Configuration
public class InterceptorConfig extends WebMvcConfigurationSupport {

    @Autowired
    private AuthenticationInterceptor authenticationInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authenticationInterceptor).addPathPatterns("/**");
        super.addInterceptors(registry);
    }
}

InterceptorConfig 类用于配置拦截器,将 AuthenticationInterceptor 拦截器添加到拦截器注册器中,并指定其拦截所有路径。

三、代码验证

1、测试代码

package com.fhey.controller;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.fhey.annotation.AutoPage;
import com.fhey.base.vo.resp.RespVo;
import com.fhey.manager.login.LoginUserInfoManager;
import com.fhey.model.bo.LoginUserInfoContext;
import com.fhey.model.po.User;
import com.fhey.model.rest.request.user.UserPageQueryReq;
import com.fhey.service.pagebatch.UserPageBatchHand;
import com.fhey.service.user.UserService;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * @author fhey
 */
@RestController
@RequiredArgsConstructor
@RequestMapping("/test")
public class TestController {

    @PostMapping("/loginUserInfoManager")
    @AutoPage
    public RespVo testLoginUserInfoManager() {
        LoginUserInfoContext loginUserInfoContext = LoginUserInfoManager.get();
        System.out.println(JSON.toJSONString(loginUserInfoContext));
        return RespVo.success(loginUserInfoContext);
    }
}

TestController 中的 testLoginUserInfoManager 方法用于测试获取登录用户信息,实际业务处理可依此进行后续操作。

2、测试结果

在这里插入图片描述

四、总结

综上所述,此段代码构建了完善的登录用户信息管理和拦截机制,确保系统在处理用户请求时能精准获取和处理用户信息,并妥善进行错误处理和内存管理。

好的,关于你的问题,我可以给你一些思路和代码示例。 首先,拦截器可以通过实现`HandlerInterceptor`接口来定义。具体实现如下: ```java @Component public class UserInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 在请求处理之前进行拦截,可以在这里获取用户信息 User user = (User) request.getSession().getAttribute("user"); // 假设用户信息存在session中 if (user == null) { response.sendRedirect("/login"); // 如果用户未登录,则跳转到登录页面 return false; } return true; } // 其他方法省略 } ``` 以上代码中,我们通过实现`preHandle`方法,在请求处理之前进行拦截。在这个方法中,我们可以通过`HttpServletRequest`对象获取到当前请求的信息,比如请求头、请求参数、Session等信息。如果我们在登录时将用户信息存储在Session中,那么我们可以通过`request.getSession().getAttribute("user")`来获取当前登录用户的信息。 接下来,我们需要将拦截器注册到Spring Boot中。这可以通过在配置类中添加`@Bean`注解来完成。具体实现如下: ```java @Configuration public class InterceptorConfig implements WebMvcConfigurer { @Autowired private UserInterceptor userInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(userInterceptor).addPathPatterns("/**").excludePathPatterns("/login", "/register"); // 设置拦截的路径 } } ``` 以上代码中,我们通过实现`WebMvcConfigurer`接口,将拦截器注册到Spring Boot中。我们使用`addInterceptors`方法来添加拦截器,并通过`addPathPatterns`方法来指定需要拦截的路径。在这里,我们设置拦截所有路径,但是排除了登录和注册页面,因为这些页面不需要用户登录即可访问。 最后,需要注意的是,我们在拦截器获取用户信息时,需要确保用户已经登录。如果用户未登录,则需要将请求重定向到登录页面。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

知北游z

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

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

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

打赏作者

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

抵扣说明:

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

余额充值