总说
过程参考黑马程序员SpringBoot3+Vue3全套视频教程,springboot+vue企业级全栈开发从基础、实战到面试一套通关_哔哩哔哩_bilibili
目录
一、基本功能实现
1.1 controller层
进入我们的UserController,我们再写一个方法
代码如下:
//获取用户信息
//@RequestHeader(name = "Authorization") 是从请求头中获取Authorization的值
@GetMapping("/userInfo")
public Result<User> userInfo(@RequestHeader(name = "Authorization") String token) {
//根据用户名查询用户
Map<String, Object> map = JwtUtil.parseToken(token);//传入token 解析token
String username = (String) map.get("username");//获取用户名
User user = userService.findByUserName(username);//返回一个查找到的User对象
return Result.success(user);
}
service、impl、mapper层的方法我们之前登录的时候写过了。
1.2 测试接口
直接进行测试
先不传任何参数,会得到401,因为我们没有请求头,它获取不到参数
所以,我们先通过登录接口,登录一下,账号还是admin 123456(数据库中已有的)
得到token
然后复制到这个获取用户信息的请求头中,
点击测试,可以得到用户的信息,成功。
1.3 调整apifox
我们发现每次测试一个接口,都要去登录一下获得token,再手动添加Authorization,非常麻烦,可以添加相同的请求头。
点击设置
点击全局参数,将Authorization写入到里面,值就是刚刚测试用的token
如下图:
这时候回到原来的接口测试
就会发现下面多了参数,我们以后就不用再手动添加Authorization
1.4 不返回密码
由于我们之前测试的时候,将用户的所有信息都返回了包括密码,这是不安全的,所有我们要设置一下不返回密码
来到pojo层目录下的User,
在password上添加一个注解@JsonIgnore
重启项目,测试一下
不再包含密码
1.5 mabits命名映射
我们发现返回的数据中,createTime 和 updateTime也是空
这是因为我们pojo中是驼峰命名,而数据库中是下划线隔开的
我们添加一下配置转化一下即可
打开application.yml,添加代码:
mybatis:
configuration:
map-underscore-to-camel-case: true # 开启 驼峰命名 和 下划线命名 自动转换
重启项目,测试一下
时间不再为空,成功。
二、代码优化
我们在写userInfo接口时,又解析了一遍token,但是在拦截器中,我们已经解析过了
我们可以用ThreadLocal来进行优化
写一个test测试验证一下
代码如下:
public class ThreadLocalTest {
@Test
public void testThreadLocalDetAndGet(){
//提供一个ThreadLocal对象
ThreadLocal tl = new ThreadLocal();
//开启两个线程
new Thread(()->{
tl.set("萧炎");//设置值
System.out.println(Thread.currentThread().getName() + ": " + tl.get());//获取值
System.out.println(Thread.currentThread().getName() + ": " + tl.get());//获取值
}, "线程1").start();//开启线程1
new Thread(()->{
tl.set("药尘");//设置值
System.out.println(Thread.currentThread().getName() + ": " + tl.get());//获取值
System.out.println(Thread.currentThread().getName() + ": " + tl.get());//获取值
}, "线程2").start();//开启线程2
}
}
运行一下测试结果
可以看到,虽然用的同一个ThreadLocal中存取数据,但是做到了线程隔离的效果
如何运用在项目中呢?我们每次调用controller、service等的方法时,都需要传入参数,很麻烦。
如果在拦截器中直接用线程声明线程局部变量,其他层用到时可以直接用.get()方法得到参数。
而且因为线程安全,每个线程的变量是独立的
根据以上,我们可以对我们的代码进行优化
2.1 代码优化
先在utils目录下添加一个工具类ThreadLocalUtil
代码如下:
@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();
}
}
然后来到拦截器LoginInterceptor
只用添加一行代码
//可以把业务数据存储到ThreadLocal中
ThreadLocalUtil.set(claims);
然后去UserController找到刚刚写的userInfo方法,修改代码如下:
//获取用户信息
//@RequestHeader(name = "Authorization") 是从请求头中获取Authorization的值
@GetMapping("/userInfo")
public Result<User> userInfo(/* @RequestHeader(name = "Authorization") String token */) {
/*
//根据用户名查询用户
Map<String, Object> map = JwtUtil.parseToken(token);//传入token 解析token
String username = (String) map.get("username");//获取用户名
User user = userService.findByUserName(username);//返回一个查找到的User对象
return Result.success(user);
*/
Map<String, Object> map = ThreadLocalUtil.get();//获取ThreadLocal中的数据
String username = (String) map.get("username");
User user = userService.findByUserName(username);//返回一个查找到的User对象
return Result.success(user);
}
启动项目,再去用apifox测试
如果显示401,可能是token过期了,因为我们之前设置的是12小时,解决办法是
1、每12小时,去登录一下得到新token更换全局变量的值
2、把token有效时间设置的长一点,但是到期仍然需要更换
那我们什么时候清空ThreadLocal呢?
再处理完请求之后清空
我们要在LoginInterceptor类中再写一个方法
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//在请求处理完成之后被调用,可以在这里进行一些后置处理,比如清理ThreadLocal中的数据
ThreadLocalUtil.remove();
}
完成后上传git