(回到IDEA中)
(1)回到UserController层的"/userInfo"接口。
- 注释掉之前写的方法参数(请求头)。
- 注释掉token解析代码。
(2)使用一个ThreadLocal工具类。
- 为了使用方便,我使用了一个ThreadLocal的工具类。将类复制到utils包下。
- 首先看到下面的工具类。它提供了一个常量"THREAD_LOCAL"。这个就是用来维护一个全局唯一的ThreadLocal对象。
- 然后有一个get()方法,把得到的数据返回回去。而且还是用的是一个泛型。(如果声明的是String,它会强转为String。声明的Map,强转Map)因为ThreadLocal可以存储任意类型的数据。
- 还提供了一个set()方法,存储值调用set()方法。
- 注意:提供了一个remove()方法,它就是调用了ThreadLocal中的remove()方法。它是用来清除之前存的数据。因为ThreadLocal设定的时候是唯一的(全局变量),那么它的生命周期特别的长。如果用完了不清除,就会一直驻留,可能造成内存泄漏问题。
package com.feisi.utils;
import java.util.HashMap;
import java.util.Map;
/**
* ThreadLocal 工具类
*/
@SuppressWarnings("all")
public class ThreadLocalUtil {
//提供ThreadLocal对象,
private static final ThreadLocal THREAD_LOCAL = new ThreadLocal();
//根据键获取值
public static <T> T get(){
return (T) THREAD_LOCAL.get();
}
//存储键值对
public static void set(Object value){
THREAD_LOCAL.set(value);
}
//清除ThreadLocal 防止内存泄漏
public static void remove(){
THREAD_LOCAL.remove();
}
}
(3)回到拦截器LoginInterceptor中。
- 在解析得到的业务数据后,将业务数据存储到ThreadLocal中。
- 利用到上面的ThreadLocal工具类。
(4)再次回到UserController层的"/userInfo"接口。
- 刚刚在拦截器存储的值是Map类型的。
- 现在通过get()方法获取,并用变量接收即可。
- 再获取指定的属性"username"即可。
@GetMapping("/userInfo")
public Result<User> userInfo(/*@RequestHeader(name = "Authorization") String token*/){
//方法内部根据用户名查询用户
/* Map<String, Object> map = JwtUtil.parseToken(token);
String username = map.get("username").toString();*/
Map<String,Object> map = ThreadLocalUtil.get();
String username = map.get("username").toString();
//拿到username就去调用service层的据用户名查询用户方法
User user = userService.findByName(username);
return Result.success(user);
}
(5)重启工程进行接口测试。
- 注意。登录认证生成的"JWT令牌"可能测试时已经过期,之前设定的是12个小时。所以在postman中测试接口时,需要重新生成一个"JWT令牌",然后在去统一设置请求头。
- 再次测试接口成功获取数据。
(访问"localhost:8080/user/userInfo")
(6)问题。
(I)分析。
- 当我们用完数据要记得去清除这个数据。
- 应该在哪个位置去清楚???(remove()方法)
- 分析:拦截器中,解析成功携带的token令牌后,将它存储到ThreadLocal中。当请求放行了(return了true)之后。在Controller、Service、Dao层中都可以使用到这个共享数据。当响应完成了之后,也就是这一次请求结束了就不再使用了。
- 所以我们应该是请求完成了,然后把数据清除掉。
(II)重写拦截器中的afterCompletion()方法。
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//清空ThreadLocal中的数据
//防止内存泄漏
ThreadLocalUtil.remove();
}
(III)整个拦截器的代码
package com.feisi.interceptors;
import com.feisi.pojo.Result;
import com.feisi.utils.JwtUtil;
import com.feisi.utils.ThreadLocalUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import java.util.Map;
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//令牌验证
String token = request.getHeader("Authorization");
//解析token
//用提供的工具类解析和验证token
try {
Map<String, Object> claims = JwtUtil.parseToken(token);
//将得到的业务数据存储到ThreadLocal中
ThreadLocalUtil.set(claims);
//放行
return true;
} catch (Exception e) {
//设置http响应状态码为401
response.setStatus(401);
//不放行
return false;
}
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//清空ThreadLocal中的数据
//防止内存泄漏
ThreadLocalUtil.remove();
}
}