https://www.bilibili.com/video/BV1f3411G7xk
公司之前是以JWT + ThreadLocal 做的登录系统,在使用的过程发现了如下的问题,下面我们一起来看看,后面也会给出更好的解决方案。
所谓的JWT是 json web token
的缩写,你可以理解成把一个数据进行一系列的加密后生成的一个字符串,所以你也可以把它解密成原本的数据。
这个可能很多人听过,但却很少人用过,其实很简单,在多线程的情况下,如果你不想用同步的方式解决就可以用ThreadLocal线程本地变量来解决并发的问题。
你可以理解成它是一个特殊的map,它的key就是线程本身,value就是你想存储的数据。
实现起来很简单,一个登录接口生成token
,一个拦截器,每次解析token放入当前线程的 threadLocal
,然后在方法里面随时随地的获取当前用户信息了。
很简单,只要我们定义一个类,给它设置静态方法,就OK了。
public class UserSessionContext {
private static ThreadLocal<UserSessionVO> userSessionVOThreadLocal = new ThreadLocal<>();
public static void set(UserSessionVO userSessionVO) {
userSessionVOThreadLocal.set(userSessionVO);
}
public static UserSessionVO get() {
return userSessionVOThreadLocal.get();
}
public static void remove() {
userSessionVOThreadLocal.remove();
}
}
所谓内存泄漏就是很多无用的数据,也无法被回收。
在Java里面,有四种引用
我们的线程并不是频繁创建的,是有一个工作线程池的,当一个线程处理完后就被放回了线程池,下一个线程又调用 set
方法设置数据,而之前的数据并没有被释放,因为是new
创建的所以还有一个强引用
指向它,导致它无法被使用却也不能被回收。
解决办法: 在拦截器的后处理里面调用threadlocal的remove
方法删除即可
因为在最开始的方案里面,没有在拦截器后处理去 remove
ThreadLocal里面的数据,而又存在某些请求既可以登陆又可以不登陆,就导致后面的请求使用了之前的登陆数据,导致数据异常。
一般系统都是有用户端和管理端,我们在管理端可以修改用户权限,修改后我们同时更新JWT,这样访问管理端是没有问题。
但是在用户端就不行,没办法清除数据就导致用户端的权限不一致了。
其实现在大部分都是使用redis做数据存储,token一个随机字符串就搞定了,但有时候我们没必要或者不想引入新的技术。另外本地内存肯定是要更快的。
上述的问题也都有办法可以解决的,想一想?在逻辑上都可以解决的。
设想一个这样的场景,我们后台服务有30+,并不是所有接口都需要进行权限校验(大部分接口是需要进行权限校验的),你应当如何设置一个完美的结局方案呢?
思想还是和之前一样,生成一个token(uuid/jwt),然后一个拦截器进行拦截,再来一个自定义注解判断是否需要拦截。
不同在于,我们把这个生成token、解析token的步骤抽离出来,做成一个公共包,这样每个需要用到的服务直接引入,然后在需要拦截的方法加一个注解就好了。
关于登录部分一些公共的部分就放在这里,比如登录拦截注解、自定义异常、返回的ResponseObj
类(就是一个自定义返回值里面有 code、data、message)
之所以单独拎出来也是为了好维护,因为我们的client
和 service
都要引入,后续也可能会对基础包里面新增修改啥的,这样就只需要维护一个公共包就好了,不用修改两处。
这个服务很简单,但是很重要,它是一个独立运行提供http服务的服务。
它提供两个主要的接口
这个是很巧妙的一部分,我们把client使用 mvn deploy
命令推到远程仓库,这样谁需要使用,直接在pom里面引入就好了。
我们项目引入了 client,而client引入了 common,所以我们项目也可以使用自定义注解,只需要在需要拦截的地方加入自定义登录拦截注解就好了。
把之前的拦截器复制到client里面,对于解析token的地方改成使用http调用service服务(当然也可以使用openFeign这样的方式调用)。
底包有一个很重要的思想:引入就使用,如果不使用就不要引入,所以我们需要在自动注入里面直接配置好注入这个拦截器。
因为这个涉及到公司源码,不方面公开,原理就是这样,可以根据原理搭建一个,前提准备知识是关于 starter 的构建,可以看看这个 https://blog.csdn.net/Tomwildboar/article/details/122901248
在写这个文章的时候,突然想到单点登录,毕竟都是解决登录问题嘛,有什么区别呢?
Java进阶之单点登录详解