feat(auth): 新增用户ID上下文过滤器及登出逻辑优化

- 新增 GlobalConstants 常量类定义 USER_ID 常量
- 新增 HeaderUserId2ContextFilter 过滤器从请求头获取用户 ID 并存入 ThreadLocal
- 新增 LoginUserContextHolder 工具类用于管理用户 ID 的 ThreadLocal 操作
-优化 UserController 的 logout 方法,移除手动传参 userId,改为从上下文获取
- 优化 UserServiceImpl 的 logout 方法实现,通过上下文获取用户 ID 完成登出
- 在 han-note-gateway 模块中添加 tomcat-embed-core依赖以支持相关功能
This commit is contained in:
Hanserwei
2025-10-02 22:27:49 +08:00
parent 3cc615d38a
commit d448c524b7
7 changed files with 112 additions and 6 deletions

View File

@@ -27,9 +27,7 @@ public class UserController {
@PostMapping("/logout")
@ApiOperationLog(description = "账号登出")
public Response<?> logout(@RequestHeader("userId") String userId) {
log.info("==> 网关透传过来的用户 ID: {}", userId);
Long userIdLong = Long.parseLong(userId);
return userService.logout(userIdLong);
public Response<?> logout() {
return userService.logout();
}
}

View File

@@ -0,0 +1,47 @@
package com.hanserwei.hannote.auth.filter;
import com.hanserwei.framework.common.constant.GlobalConstants;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
@Component
@Slf4j
public class HeaderUserId2ContextFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {
// 从请求头中获取用户 ID
String userId = request.getHeader(GlobalConstants.USER_ID);
log.info("## HeaderUserId2ContextFilter, 用户 ID: {}", userId);
// 判断请求头中是否存在用户 ID
if (StringUtils.isBlank(userId)) {
// 若为空,则直接放行
chain.doFilter(request, response);
return;
}
// 如果 header 中存在 userId则设置到 ThreadLocal 中
log.info("===== 设置 userId 到 ThreadLocal 中, 用户 ID: {}", userId);
LoginUserContextHolder.setUserId(userId);
try {
chain.doFilter(request, response);
} finally {
// 一定要删除 ThreadLocal ,防止内存泄露
LoginUserContextHolder.remove();
log.info("===== 删除 ThreadLocal userId: {}", userId);
}
}
}

View File

@@ -0,0 +1,45 @@
package com.hanserwei.hannote.auth.filter;
import com.hanserwei.framework.common.constant.GlobalConstants;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
public class LoginUserContextHolder {
// 初始化一个 ThreadLocal 变量
private static final ThreadLocal<Map<String, Object>> LOGIN_USER_CONTEXT_THREAD_LOCAL
= ThreadLocal.withInitial(HashMap::new);
/**
* 设置用户 ID
*
* @param value 用户 ID
*/
public static void setUserId(Object value) {
LOGIN_USER_CONTEXT_THREAD_LOCAL.get().put(GlobalConstants.USER_ID, value);
}
/**
* 获取用户 ID
*
* @return 用户 ID
*/
public static Long getUserId() {
Object value = LOGIN_USER_CONTEXT_THREAD_LOCAL.get().get(GlobalConstants.USER_ID);
if (Objects.isNull(value)) {
return null;
}
return Long.valueOf(value.toString());
}
/**
* 删除 ThreadLocal
*/
public static void remove() {
LOGIN_USER_CONTEXT_THREAD_LOCAL.remove();
}
}

View File

@@ -19,5 +19,5 @@ public interface UserService extends IService<UserDO> {
* 退出登录
* @return 响应结果
*/
Response<?> logout(Long userId);
Response<?> logout();
}

View File

@@ -21,6 +21,7 @@ import com.hanserwei.hannote.auth.domain.mapper.UserDOMapper;
import com.hanserwei.hannote.auth.domain.mapper.UserRoleDOMapper;
import com.hanserwei.hannote.auth.enums.LoginTypeEnum;
import com.hanserwei.hannote.auth.enums.ResponseCodeEnum;
import com.hanserwei.hannote.auth.filter.LoginUserContextHolder;
import com.hanserwei.hannote.auth.model.vo.user.UserLoginReqVO;
import com.hanserwei.hannote.auth.service.UserService;
import lombok.RequiredArgsConstructor;
@@ -144,7 +145,8 @@ public class UserServiceImpl extends ServiceImpl<UserDOMapper, UserDO> implement
}
@Override
public Response<?> logout(Long userId) {
public Response<?> logout() {
Long userId = LoginUserContextHolder.getUserId();
StpUtil.logout(userId);
return Response.success();
}