文章目录
一、引言
在系统运行中,获取登录用户信息是常见需求,但此操作常与业务代码无关,且在一次请求链路中可能多次使用。因此,采用拦截器结合 ThreadLocal
的方式来优化处理,其逻辑如下:于请求执行前,通过拦截器拦截请求,依据请求中的 token
或 userId
获取用户信息并存入 ThreadLocal
;在请求执行后,再次通过拦截器拦截请求,清除 ThreadLocal
中对应请求的用户信息。如此,实现了获取登录用户信息与业务代码的解耦,以及单次请求中用户信息的复用。
二、代码逻辑
1、依赖引入
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
<version>2.12.6</version>
</dependency>
引入了 com.alibaba
的 transmittable-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、测试结果
四、总结
综上所述,此段代码构建了完善的登录用户信息管理和拦截机制,确保系统在处理用户请求时能精准获取和处理用户信息,并妥善进行错误处理和内存管理。