1.通用实体类
package com.sundark.mylife.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* @author wwujiada
*/
@Data
@Accessors(chain = true)
public class BaseDO implements Serializable {
// 主键
@TableId(type = IdType.AUTO)
private Integer id;
/**
* 创建时间
*/
@TableField(value = "create_time", fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
* 创建人id
*/
@TableField(value = "creator", fill = FieldFill.INSERT)
private Integer creator;
/**
* 更新时间
*/
@TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
/**
* 更新人id
*/
@TableField(value = "updater", fill = FieldFill.INSERT_UPDATE)
private Integer updater;
// 逻辑删除字段,MyBatis-Plus 3.x 默认使用 LogicDelete 注解,并识别字段名为 deleted 或 is_deleted
@TableLogic
private Boolean deleted;
}
2.TreadLocal类
package com.sundark.mylife.utils;
/**
* @author wujiada
* 线程私有对象
*/
public class ThreadLocalUtil {
private final static ThreadLocal<Object> USER_THREAD_LOCAL = new ThreadLocal<>();
/**
* 设置数据到当前线程
*/
public static void set(Object o) {
USER_THREAD_LOCAL.set(o);
}
/**
* 获取线程中的数据
*/
public static Object get() {
return USER_THREAD_LOCAL.get();
}
/**
* 删除线程中的数据
*/
public static void remove() {
USER_THREAD_LOCAL.remove();
}
}
3.mybatis-plus配置类,从TreadLocal中取出用户信息
package com.sundark.mylife.config;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.sundark.mylife.constants.SysConstant;
import com.sundark.mylife.entity.BaseDO;
import com.sundark.mylife.entity.model.MlUserDO;
import com.sundark.mylife.enums.CodeEnum;
import com.sundark.mylife.exception.ServiceException;
import com.sundark.mylife.utils.ThreadLocalUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.LocalDateTime;
import java.util.Objects;
/**
* @author wujiada
* mybatis-plus自动填充字段配置
*/
@Configuration
@Slf4j
public class MybatisPlusConfig {
/**
* 自动填充创建者、更新者等字段
*
* @return 自定义的 MetaObjectHandler
*/
@Bean
public MetaObjectHandler metaObjectHandler() {
return new MetaObjectHandler() {
@Override
public void insertFill(MetaObject metaObject) {
if (Objects.nonNull(metaObject) && metaObject.getOriginalObject() instanceof BaseDO baseDO) {
LocalDateTime current = LocalDateTime.now();
// 创建时间为空,则以当前时间为插入时间
if (Objects.isNull(baseDO.getCreateTime())) {
baseDO.setCreateTime(current);
}
// 更新时间为空,则以当前时间为更新时间
if (Objects.isNull(baseDO.getUpdateTime())) {
baseDO.setUpdateTime(current);
}
// ThreadLocal中获取用户id
MlUserDO userDO = (MlUserDO) ThreadLocalUtil.get();
Integer userId;
// 注册的时候没有用户id,默认使用系统用户id
if (userDO != null) {
userId = userDO.getId();
} else {
userId = SysConstant.ADMIN_ID;
}
// 当前登录用户不为空,创建人为空,则当前登录用户为创建人
if (Objects.nonNull(userId) && Objects.isNull(baseDO.getCreator())) {
baseDO.setCreator(userId);
}
// 当前登录用户不为空,更新人为空,则当前登录用户为更新人
if (Objects.nonNull(userId) && Objects.isNull(baseDO.getUpdater())) {
baseDO.setUpdater(userId);
}
// 当前删除flag为null
if (Objects.nonNull(userId) && Objects.isNull(baseDO.getUpdater())) {
baseDO.setUpdater(userId);
}
}
}
@Override
public void updateFill(MetaObject metaObject) {
// 更新时间为空,则以当前时间为更新时间
Object modifyTime = getFieldValByName("updateTime", metaObject);
if (Objects.isNull(modifyTime)) {
setFieldValByName("updateTime", LocalDateTime.now(), metaObject);
}
// 当前登录用户不为空,更新人为空,则当前登录用户为更新人
Object modifier = getFieldValByName("updater", metaObject);
// ThreadLocal中获取用户id
MlUserDO userDO = (MlUserDO) ThreadLocalUtil.get();
// 更新说明该用户登录了
if (userDO == null) {
throw new ServiceException(CodeEnum.USER_NOT_EXISTS);
}
Integer userId = userDO.getId();
if (Objects.nonNull(userId) && Objects.isNull(modifier)) {
setFieldValByName("updater", userId, metaObject);
}
}
};
}
}
4.鉴权过滤器,将用户信息存储到TreadLocal中
package com.sundark.mylife.filter;
import com.sundark.mylife.constants.SysConstant;
import com.sundark.mylife.entity.model.MlUserDO;
import com.sundark.mylife.utils.StringUtil;
import com.sundark.mylife.utils.ThreadLocalUtil;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.io.IOException;
import static com.sundark.mylife.utils.JwtUtil.isValidToken;
/**
* @author wujiada
* 鉴权过滤器
*/
@Component
@Order(1)
@Slf4j// 数字越大,优先级越低
public class AuthFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
/**
* 处理请求
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// 假设登录请求的URL是/login
// /user/api/v1/user/login 和/register
String uri = httpRequest.getRequestURI();
if (uri.contains("/doc.html") || uri.startsWith("/swagger-ui.html") || uri.startsWith("/v3/api-docs") || uri.startsWith("/swagger-resources/") ||
uri.contains(SysConstant.LOGIN_URL) || uri.contains(SysConstant.REGISTER_URL)) {
// 登录/注册请求,放行
chain.doFilter(request, response);
} else {
// 对于非登录注册请求,进行鉴权
String token = httpRequest.getHeader("token");
String userId = isValidToken(token);
if (StringUtil.isEmpty(userId)) {
// 鉴权失败,返回未授权响应
httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
httpResponse.getWriter().write("Unauthorized access");
return;
}
// 登录用户存入ThreadLocal
MlUserDO user = new MlUserDO();
user.setId(Integer.valueOf(userId));
ThreadLocalUtil.set(user);
try {
// 放行
chain.doFilter(request, response);
} finally {
// 手动移除ThreadLocal内存数据,防止内存溢出
ThreadLocalUtil.remove();
}
}
}
@Override
public void destroy() {
Filter.super.destroy();
}
}