1.工具类
package com.sky.context;
public class BaseContext {
public static ThreadLocal<Long> threadLocal = new ThreadLocal<>();
public static void setCurrentId(Long id) {
threadLocal.set(id);
}public static Long getCurrentId() {
return threadLocal.get();
}public static void removeCurrentId() {
threadLocal.remove();
}}
2.拦截器
package com.sky.interceptor;
import com.sky.constant.JwtClaimsConstant;
import com.sky.context.BaseContext;
import com.sky.properties.JwtProperties;
import com.sky.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
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 javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/**
* jwt令牌校验的拦截器
*/
@Component
@Slf4j
public class JwtTokenAdminInterceptor implements HandlerInterceptor {@Autowired
private JwtProperties jwtProperties;/**
* 校验jwt
* 在Handler之前执行。就是Controller中标记了@XxxMapping注解的方式
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//判断当前拦截到的是Controller的方法还是其他资源
if (!(handler instanceof HandlerMethod)) {
//当前拦截到的不是动态方法,直接放行
return true;
}//1、从请求头中获取令牌
String token = request.getHeader(jwtProperties.getAdminTokenName());//2、校验令牌
try {
log.info("jwt校验:{}", token);
Claims claims = JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token);
Long empId = Long.valueOf(claims.get(JwtClaimsConstant.EMP_ID).toString());
log.info("当前员工id:", empId);//把登录ID设置到ThreadLocal上
BaseContext.setCurrentId(empId);
//3、通过,放行
return true;
} catch (Exception ex) {
//4、不通过,响应401状态码
response.setStatus(401);
return false;
}
}@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//从ThreadLocal上删除登录ID
BaseContext.removeCurrentId();
}
}
3.service调用
package com.sky.service.impl;
@Service
public class EmployeeServiceImpl implements EmployeeService {@Autowired
private EmployeeMapper employeeMapper;@Override
public Result<String> save(EmployeeDTO employeeDTO) {
//1、校验参数
String username = employeeDTO.getUsername();
String name = employeeDTO.getName();
String sex = employeeDTO.getSex();
String phone = employeeDTO.getPhone();if (StringUtils.isBlank(username) //username == null || "".equlas(username)
|| StringUtils.isBlank(name)
|| StringUtils.isBlank(sex)
|| StringUtils.isBlank(phone)) {
throw new ArgsErrorException(MessageConstant.ARGS_ERROR);
}//2、处理业务
//2.1 判断当前用户名是否已经被注册了
Employee employee = employeeMapper.getByUsername(username);
//如果重复,直接返回:用户名已存在
if(!Objects.isNull(employee)) {
throw new AccountExistsException(MessageConstant.ACCOUNT_FOUND);
}
//2.2 补全实体属性
Employee emp = new Employee();
//拷贝的属性名跟类型必须一模一样
BeanUtils.copyProperties(employeeDTO,emp);
emp.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes()));
emp.setStatus(StatusConstant.ENABLE);
LocalDateTime now = LocalDateTime.now();
emp.setCreateTime(now);
emp.setUpdateTime(now);
// TODO 后面需要动态获取登录用户的ID
Long id = BaseContext.getCurrentId();
emp.setCreateUser(id);
emp.setUpdateUser(id);
//2.3 保存
employeeMapper.save(emp);//3、封装数据
return Result.success(MessageConstant.SAVE_SUCCESS);
}}
4.内存泄漏问题?
ThreadLocal是弱引用,在调用gc垃圾处理器的时候,会自动回收。
我们只需要在拦截器的渲染后的方法中调用remove方法即可解决。