UserAutoConfiguration
UserAutoConfiguration
,用于注册一个全局的用户信息传输过滤器 UserTransmitFilter
。该类只用于web应用(@ConditionalOnWebApplication)
UserTransmitFilter
: 这是一个 Servlet 过滤器,用于从 HTTP 请求中提取用户信息并存储到UserContext
中。doFilter
方法:- 从请求头中获取用户 ID、用户名、真实姓名和 Token。
- 如果获取到用户 ID,则构建
UserInfoDTO
对象并存入UserContext
。 - 最后,调用
filterChain.doFilter
继续处理请求,确保在请求处理完成后清理用户信息。
UserContext
它使用了 ThreadLocal
来存储当前线程的用户信息,以便在整个请求处理过程中能够方便地访问用户相关的数据。
Theadlocal是什么?有哪些使用场景?底层实现是什么?-CSDN博客
private static final ThreadLocal<UserInfoDTO> USER_THREAD_LOCAL = new TransmittableThreadLocal<>();
ThreadLocal<UserInfoDTO>
:ThreadLocal
是 Java 提供的一个类,用于在每个线程中存储独立的变量。每个线程都有自己的UserInfoDTO
实例,避免了线程间的干扰。TransmittableThreadLocal
: 这是ThreadLocal
的一个扩展,支持在异步操作中传递值。它可以在不同的线程之间传递用户信息,适用于高并发场景中的异步请求处理。
public class AsyncTaskExecutor {
private static final ThreadLocal<UserInfo> USER_INFO_THREAD_LOCAL = new TransmittableThreadLocal<>();
public static void submitTask(Runnable task) {
// 在提交任务之前,先设置用户信息
UserInfo userInfo = new UserInfo("user123", "John Doe");
USER_INFO_THREAD_LOCAL.set(userInfo);
// 提交任务
new Thread(task).start();
}
public static void processTask() {
// 在处理任务时,可以获取到用户信息
UserInfo userInfo = USER_INFO_THREAD_LOCAL.get();
System.out.println("Processing task for user: " + userInfo.getUserId() + " (" + userInfo.getUsername() + ")");
// 模拟处理任务
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
我们使用了 TransmittableThreadLocal
来存储用户信息。当我们提交一个异步任务时,会先设置用户信息,然后启动一个新线程来执行任务。
AsyncTaskExecutor.submitTask(() -> {
AsyncTaskExecutor.processTask();
});
在任务的处理方法 processTask()
中,我们可以通过 USER_INFO_THREAD_LOCAL.get()
获取到之前设置的用户信息。这是因为 TransmittableThreadLocal
会自动将用户信息传递到新的线程中。
这个类使用 Optional
来避免空指针异常,如果用户信息为空,则返回 null
。
UserTransmitFilter
UserTransmitFilter
是一个实现了 Filter
接口的过滤器,负责在每个 HTTP 请求中提取用户信息并将其存储到用户上下文中。
在UserAutoConfiguration中我们注册了UserTransmitFilter,所有的http请求都会经过这个过滤器,优先级为100。
在 doFilter
方法中,过滤器首先从请求中提取用户信息:
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
ServletRequest
类型的 servletRequest
转换为 HttpServletRequest
类型。HttpServletRequest
是一个更具体的接口,提供了处理 HTTP 请求的特定方法,比如获取请求头、请求参数等。
UserContext.setUser(userInfoDTO);
}
try {
filterChain.doFilter(servletRequest, servletResponse);
} finally {
UserContext.removeUser();
}
UserContext.setUser(userInfoDTO)
将用户信息存储到上下文中后,后续的过滤器以及其他处理逻辑(如控制器、服务层等)都可以直接从 UserContext
中获取用户信息
在 Spring Web 应用中,filterChain.doFilter(servletRequest, servletResponse)
是用来将请求传递给下一个过滤器或目标资源(如控制器)的。如果当前过滤器是过滤器链中的最后一个过滤器将直接将请求传递给目标资源(如控制器)
控制器的响应返回后,LastFilter
和 FirstFilter
的 doFilter
方法继续执行 finally
块中的逻辑,比如这里的UserContext.removeUser();即使的remove防止内存泄漏。
JWTUtil
JWTUtil
是一个用于生成和解析 JSON Web Token (JWT) 的工具类。在现代 Web 应用中,JWT 常用于用户身份验证和授权。
@Slf4j
public final class JWTUtil {
private static final long EXPIRATION = 86400L; // Token 过期时间(单位:秒)
public static final String TOKEN_PREFIX = "Bearer "; // Token 前缀
public static final String ISS = "index12306"; // Token 发行者
public static final String SECRET = "SecretKey039245678901232039487623456783092349288901402967890140939827"; // 签名密钥
}
EXPIRATION
:定义了 Token 的有效期(86400 秒,即 24 小时)。TOKEN_PREFIX
:用于标识 Token 的前缀,通常在 HTTP 头中使用。ISS
:定义了 Token 的发行者,通常是应用的标识。SECRET
:用于签名和验证 Token 的密钥,确保 Token 的安全性。
SECRET
通常是随机生成的字符串,可以使用加密库(如 Java 的 SecureRandom
)来生成一个强随机密钥。
import java.security.SecureRandom;
import java.util.Base64;
public class SecretGenerator {
public static String generateSecret() {
byte[] randomBytes = new byte[32]; // 256 bits
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(randomBytes);
return Base64.getEncoder().encodeToString(randomBytes);
}
}
SECRET
应该足够长(至少 256 位)并且复杂,以防止暴力破解。通常,使用 Base64 编码的随机字节串是一个好的选择。
String jwtToken = Jwts.builder()
.signWith(SignatureAlgorithm.HS512, SECRET)
.setIssuedAt(new Date())
.setIssuer(ISS)
.setSubject(JSON.toJSONString(customerUserMap))
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION * 1000))
.compact();
return TOKEN_PREFIX + jwtToken;
指定签名算法为 HMAC SHA-512(HS512
),并使用 SECRET
作为签名密钥。这个密钥用于生成和验证 JWT 的有效性。
设置 JWT 的签发时间为当前时间。
设置 JWT 的发行者(iss
声明),通常是应用的标识,这里使用常量 ISS
。
设置 JWT 的主题(sub
声明),这里将用户信息的 map 转换为 JSON 字符串,并作为主题存储在 JWT 中。
设置 JWT 的过期时间,当前时间加上 EXPIRATION
(单位为秒),表示 Token 的有效期。
构建并返回 JWT 字符串,compact()
方法将所有设置的属性组合成一个 JWT。