本插件作为官方监听器功能的补充.
默认监听器只能在用户调用 logout() 方法且未下线时触发注销事件,
SaToken 框架不会因为(全局或临时)过期而自动触发注销事件.
本插件就是为了解决此问题.
插件源码:
public class WatchDogPlugin {
private static Map<String, LoginObj> map = new HashMap<>(); // token->登录信息
private static Integer timeInterval = 1;
private static Thread refreshThread;
public static void start() {
if (refreshThread == null) {
// 新注册监听器(与开发者监听器共存)
SaTokenEventCenter.registerListener(new SaTokenListener() {
@Override
public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) {
saveLoginInfo(loginType, loginId, tokenValue, loginModel);
}
@Override
public void doLogout(String loginType, Object loginId, String tokenValue) {
invalidToken(loginType, loginId, tokenValue);
}
@Override
public void doKickout(String loginType, Object loginId, String tokenValue) {
}
@Override
public void doReplaced(String loginType, Object loginId, String tokenValue) {
}
@Override
public void doDisable(String loginType, Object loginId, String service, int level, long disableTime) {
}
@Override
public void doUntieDisable(String loginType, Object loginId, String service) {
}
@Override
public void doCreateSession(String id) {
}
@Override
public void doLogoutSession(String id) {
}
@Override
public void doRenewTimeout(String tokenValue, Object loginId, long timeout) {
}
});
// 增加循环线程
resetRefreshThread();
}
}
/**
* 登录后保存信息
* @param loginType
* @param loginId
* @param tokenValue
* @param loginModel
*/
public static void saveLoginInfo(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) {
LoginObj obj = new LoginObj(loginType, loginId, loginModel.getDeviceOrDefault(), tokenValue);
map.put(tokenValue, obj);
}
/**
* 循环查看当前已登录用户状态
*/
private static void resetRefreshThread() {
refreshThread = new Thread(() -> {
while (true){
try{Thread.sleep(timeInterval*1000);}catch (Exception ignored) {}
List<String> list = new ArrayList<>();
try {
map.forEach((k, v) -> {
String token = v.getToken();
StpLogic stpLogic = SaManager.getStpLogic(v.getLoginType());
String loginId = stpLogic.getLoginIdNotHandle(token);
if(!stpLogic.isValidLoginId(loginId)) {
// 全局过期
SaTokenEventCenter.doLogout(v.getLoginType(), v.getLoginId(), token);
list.add(token);
} else {
long t = stpLogic.getTokenActivityTimeoutByToken(token);
if (t!=-1 && t<=0) {
// 临时过期
stpLogic.logout(v.getLoginId(), v.getDevice());
list.add(token);
}
}
});
}catch (ConcurrentModificationException ignored){}
list.forEach(token -> {
map.remove(token);
});
}
});
refreshThread.start();
}
/**
* 单位: 秒
* t大于等于 1
* @param t
*/
public static void setTimeInterval(Integer t) {
if(t<1) {
t = 1;
}
timeInterval = t;
}
/**
* 返回定时器间隔, 单位秒
* @return
*/
public static Integer getTimeInterval() {
return timeInterval;
}
/**
* 事件已触发则删除临时数据
*
* @param loginType
* @param loginId
* @param token
*/
public static void invalidToken(String loginType, Object loginId, String token) {
/**
* 正常logout()方法会被动触发
* 全局过期主动触发
* 临时过期间接触发
*/
if (map.containsKey(token)) {
map.remove(token);
}
}
}
此外还需要一个当前用户对象 LoginObj , 用于注销事件参数(@Data注解需要开启Lombok)
@Data
public class LoginObj {
private String loginType;
private Object loginId;
private String device;
private String token;
public LoginObj(){}
public LoginObj(String loginType, Object loginId,String device, String token) {
this.loginType = loginType;
this.loginId = loginId;
this.device = device;
this.token = token;
}
}
此时插件已经制作完成, 接下来在项目启动时开启插件就完成了
// 插件启动
WatchDogPlugin.start();
// 设置循环时间: 默认1秒检查一次(可以不设置)
WatchDogPlugin.setTimeInterval(1);
此时开发者再按照官方文档实现侦听器后即可做到过期自动触发注销事件
需要注意的时, 本插件非常消耗性能, 这也是官方框架不支持此功能的主要原因