重构注销相关API

This commit is contained in:
click33
2021-10-01 04:01:35 +08:00
parent 826760f160
commit 4ba55ff687
10 changed files with 485 additions and 183 deletions

View File

@@ -1,5 +1,7 @@
package cn.dev33.satoken.exception; package cn.dev33.satoken.exception;
import cn.dev33.satoken.util.SaFoxUtil;
/** /**
* Sa-Token框架内部逻辑发生错误抛出的异常 * Sa-Token框架内部逻辑发生错误抛出的异常
* (自定义此异常方便开发者在做全局异常处理时分辨异常类型) * (自定义此异常方便开发者在做全局异常处理时分辨异常类型)
@@ -42,4 +44,26 @@ public class SaTokenException extends RuntimeException {
super(message, cause); super(message, cause);
} }
/**
* 如果flag==true则抛出message异常
* @param flag 标记
* @param message 异常信息
*/
public static void throwBy(boolean flag, String message) {
if(flag) {
throw new SaTokenException(message);
}
}
/**
* 如果value==null或者isEmpty则抛出message异常
* @param value 值
* @param message 异常信息
*/
public static void throwByNull(Object value, String message) {
if(SaFoxUtil.isEmpty(value)) {
throw new SaTokenException(message);
}
}
} }

View File

@@ -31,18 +31,16 @@ public interface SaTokenListener {
* @param loginType 账号类别 * @param loginType 账号类别
* @param loginId 账号id * @param loginId 账号id
* @param tokenValue token值 * @param tokenValue token值
* @param device 设备标识
*/ */
public void doLogoutByLoginId(String loginType, Object loginId, String tokenValue, String device); public void doKickout(String loginType, Object loginId, String tokenValue);
/** /**
* 每次被顶下线时触发 * 每次被顶下线时触发
* @param loginType 账号类别 * @param loginType 账号类别
* @param loginId 账号id * @param loginId 账号id
* @param tokenValue token值 * @param tokenValue token值
* @param device 设备标识
*/ */
public void doReplaced(String loginType, Object loginId, String tokenValue, String device); public void doReplaced(String loginType, Object loginId, String tokenValue);
/** /**
* 每次被封禁时触发 * 每次被封禁时触发

View File

@@ -26,23 +26,23 @@ public class SaTokenListenerDefaultImpl implements SaTokenListener {
*/ */
@Override @Override
public void doLogout(String loginType, Object loginId, String tokenValue) { public void doLogout(String loginType, Object loginId, String tokenValue) {
println("账号[" + loginId + "]注销成功"); println("账号[" + loginId + "]注销成功 (Token=" + tokenValue + ")");
} }
/** /**
* 每次被踢下线时触发 * 每次被踢下线时触发
*/ */
@Override @Override
public void doLogoutByLoginId(String loginType, Object loginId, String tokenValue, String device) { public void doKickout(String loginType, Object loginId, String tokenValue) {
println("账号[" + loginId + "]被踢下线 (终端: " + device + ")"); println("账号[" + loginId + "]被踢下线 (Token=" + tokenValue + ")");
} }
/** /**
* 每次被顶下线时触发 * 每次被顶下线时触发
*/ */
@Override @Override
public void doReplaced(String loginType, Object loginId, String tokenValue, String device) { public void doReplaced(String loginType, Object loginId, String tokenValue) {
println("账号[" + loginId + "]被顶下线 (终端: " + device + ")"); println("账号[" + loginId + "]被顶下线 (Token=" + tokenValue + ")");
} }
/** /**

View File

@@ -139,6 +139,16 @@ public class SaSession implements Serializable {
update(); update();
} }
/**
* 添加一个token签名
*
* @param tokenValue token值
* @param device 设备标识
*/
public void addTokenSign(String tokenValue, String device) {
addTokenSign(new TokenSign(tokenValue, device));
}
/** /**
* 移除一个token签名 * 移除一个token签名
* *

View File

@@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.function.Consumer;
import cn.dev33.satoken.SaManager; import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.annotation.SaCheckLogin; import cn.dev33.satoken.annotation.SaCheckLogin;
@@ -22,6 +23,7 @@ import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException; import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException; import cn.dev33.satoken.exception.NotRoleException;
import cn.dev33.satoken.exception.NotSafeException; import cn.dev33.satoken.exception.NotSafeException;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.fun.SaFunction; import cn.dev33.satoken.fun.SaFunction;
import cn.dev33.satoken.session.SaSession; import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.session.TokenSign; import cn.dev33.satoken.session.TokenSign;
@@ -30,7 +32,7 @@ import cn.dev33.satoken.util.SaFoxUtil;
import cn.dev33.satoken.util.SaTokenConsts; import cn.dev33.satoken.util.SaTokenConsts;
/** /**
* Sa-Token 权限证,逻辑实现类 * Sa-Token 权限证,逻辑实现类
* @author kong * @author kong
*/ */
public class StpLogic { public class StpLogic {
@@ -97,22 +99,22 @@ public class StpLogic {
public void setTokenValue(String tokenValue, int cookieTimeout){ public void setTokenValue(String tokenValue, int cookieTimeout){
SaTokenConfig config = getConfig(); SaTokenConfig config = getConfig();
// 将token保存到[存储器]里 // 1. 将token保存到[存储器]里
SaStorage storage = SaHolder.getStorage(); SaStorage storage = SaHolder.getStorage();
// 判断是否配置了token前缀 // 如果打开了token前缀模式,则拼接上前缀一起写入
String tokenPrefix = config.getTokenPrefix(); String tokenPrefix = config.getTokenPrefix();
if(SaFoxUtil.isEmpty(tokenPrefix)) { if(SaFoxUtil.isEmpty(tokenPrefix) == false) {
storage.set(splicingKeyJustCreatedSave(), tokenValue);
} else {
// 如果配置了token前缀则拼接上前缀一起写入
storage.set(splicingKeyJustCreatedSave(), tokenPrefix + SaTokenConsts.TOKEN_CONNECTOR_CHAT + tokenValue); storage.set(splicingKeyJustCreatedSave(), tokenPrefix + SaTokenConsts.TOKEN_CONNECTOR_CHAT + tokenValue);
} else {
storage.set(splicingKeyJustCreatedSave(), tokenValue);
} }
// 注入Cookie // 2. 将token保存到[Cookie]里
if (config.getIsReadCookie()) { if (config.getIsReadCookie()) {
SaResponse response = SaHolder.getResponse(); SaResponse response = SaHolder.getResponse();
response.addCookie(getTokenName(), tokenValue, "/", config.getCookieDomain(), cookieTimeout, config.getIsCookieHttpOnly(), config.getIsCookieSecure()); response.addCookie(getTokenName(), tokenValue, "/",
config.getCookieDomain(), cookieTimeout, config.getIsCookieHttpOnly(), config.getIsCookieSecure());
} }
} }
@@ -147,12 +149,13 @@ public class StpLogic {
// 5. 如果打开了前缀模式 // 5. 如果打开了前缀模式
String tokenPrefix = getConfig().getTokenPrefix(); String tokenPrefix = getConfig().getTokenPrefix();
if(!SaFoxUtil.isEmpty(tokenPrefix) && !SaFoxUtil.isEmpty(tokenValue)) { if(SaFoxUtil.isEmpty(tokenPrefix) == false) {
// 如果token指定的前缀开头, 则裁剪掉它, 否则视为未提供token // 如果token并没有按照指定的前缀开头则视为未提供token
if(tokenValue.startsWith(tokenPrefix + SaTokenConsts.TOKEN_CONNECTOR_CHAT)) { if(SaFoxUtil.isEmpty(tokenValue) || tokenValue.startsWith(tokenPrefix + SaTokenConsts.TOKEN_CONNECTOR_CHAT) == false) {
tokenValue = tokenValue.substring(tokenPrefix.length() + SaTokenConsts.TOKEN_CONNECTOR_CHAT.length());
} else {
tokenValue = null; tokenValue = null;
} else {
// 则裁剪掉前缀
tokenValue = tokenValue.substring(tokenPrefix.length() + SaTokenConsts.TOKEN_CONNECTOR_CHAT.length());
} }
} }
@@ -182,7 +185,7 @@ public class StpLogic {
// ------------------- 登录相关操作 ------------------- // ------------------- 登录相关操作 -------------------
// 登录与注销 // --- 登录
/** /**
* 会话登录 * 会话登录
@@ -217,14 +220,15 @@ public class StpLogic {
*/ */
public void login(Object id, SaLoginModel loginModel) { public void login(Object id, SaLoginModel loginModel) {
// ------ 0、检查此账号是否已被封禁 SaTokenException.throwByNull(id, "账号id不能为空");
// ------ 0、前置检查如果此账号已被封禁.
if(isDisable(id)) { if(isDisable(id)) {
throw new DisableLoginException(loginType, id, getDisableTime(id)); throw new DisableLoginException(loginType, id, getDisableTime(id));
} }
// ------ 1、获取相应对象 // ------ 1、初始化 loginModel
SaTokenConfig config = getConfig(); SaTokenConfig config = getConfig();
SaTokenDao dao = SaManager.getSaTokenDao();
loginModel.build(config); loginModel.build(config);
// ------ 2、生成一个token // ------ 2、生成一个token
@@ -236,61 +240,47 @@ public class StpLogic {
tokenValue = getTokenValueByLoginId(id, loginModel.getDevice()); tokenValue = getTokenValueByLoginId(id, loginModel.getDevice());
} }
} else { } else {
// --- 如果不允许并发登录 // --- 如果不允许并发登录,则将这个账号的历史登录标记为:被顶下线
// 如果此时[user-session]不为null说明此账号在其他地正在登录现在需要先把其它地的同设备token标记为被顶下线 replaced(id, loginModel.getDevice());
SaSession session = getSessionByLoginId(id, false);
if(session != null) {
List<TokenSign> tokenSignList = session.getTokenSignList();
for (TokenSign tokenSign : tokenSignList) {
if(tokenSign.getDevice().equals(loginModel.getDevice())) {
// 1. 将此token 标记为已顶替
dao.update(splicingKeyTokenValue(tokenSign.getValue()), NotLoginException.BE_REPLACED);
// 2. 清理掉[token-最后操作时间]
clearLastActivity(tokenSign.getValue());
// 3. 清理user-session上的token签名记录
session.removeTokenSign(tokenSign.getValue());
// $$ 通知监听器
SaManager.getSaTokenListener().doReplaced(loginType, id, tokenSign.getValue(), tokenSign.getDevice());
}
}
}
} }
// 如果至此仍未成功创建tokenValue, 则开始生成一个 // 如果至此仍未成功创建tokenValue, 则开始生成一个
if(tokenValue == null) { if(tokenValue == null) {
tokenValue = createTokenValue(id); tokenValue = createTokenValue(id);
} }
// ------ 3. 获取[User-Session], 续期 // ------ 3. 获取 User-Session , 续期
SaSession session = getSessionByLoginId(id, true); SaSession session = getSessionByLoginId(id, true);
session.updateMinTimeout(loginModel.getTimeout()); session.updateMinTimeout(loginModel.getTimeout());
// 在session上记录token签名 // 在 User-Session 上记录token签名
session.addTokenSign(new TokenSign(tokenValue, loginModel.getDevice())); session.addTokenSign(tokenValue, loginModel.getDevice());
// ------ 4. 持久化其它数据 // ------ 4. 持久化其它数据
// token -> uid // token -> id 映射关系
dao.set(splicingKeyTokenValue(tokenValue), String.valueOf(id), loginModel.getTimeout()); saveTokenToIdMapping(tokenValue, id, loginModel.getTimeout());
// 写入 [最后操作时间] // 在当前会话写入tokenValue
setLastActivityToNow(tokenValue);
// 在当前会话写入当前tokenValue
setTokenValue(tokenValue, loginModel.getCookieTimeout()); setTokenValue(tokenValue, loginModel.getCookieTimeout());
// $$ 通知监听器 // 写入 [token-last-activity]
setLastActivityToNow(tokenValue);
// $$ 通知监听器账号xxx 登录成功
SaManager.getSaTokenListener().doLogin(loginType, id, loginModel); SaManager.getSaTokenListener().doLogin(loginType, id, loginModel);
} }
// --- 注销
/** /**
* 会话注销 * 会话注销
*/ */
public void logout() { public void logout() {
// 如果连token都没有那么无需执行任何操作 // 如果连token都没有那么无需执行任何操作
String tokenValue = getTokenValue(); String tokenValue = getTokenValue();
if(tokenValue == null) { if(SaFoxUtil.isEmpty(tokenValue)) {
return; return;
} }
// 如果打开了cookie模式第一步先把cookie清除掉 // 如果打开了Cookie模式第一步先把cookie清除掉
if(getConfig().getIsReadCookie()){ if(getConfig().getIsReadCookie()){
SaHolder.getResponse().deleteCookie(getTokenName()); SaHolder.getResponse().deleteCookie(getTokenName());
} }
@@ -298,80 +288,169 @@ public class StpLogic {
} }
/** /**
* 会话注销,根据指定Token * 会话注销,根据账号id
* @param tokenValue 指定token *
*/
public void logoutByTokenValue(String tokenValue) {
// 1. 清理掉[token-最后操作时间]
clearLastActivity(tokenValue);
// 2. 清理Token-Session
SaManager.getSaTokenDao().delete(splicingKeyTokenSession(tokenValue));
// 3. 尝试清除token-id键值对 (先从db中获取loginId值如果根本查不到loginId那么无需继续操作 )
String loginId = getLoginIdNotHandle(tokenValue);
if(loginId == null || NotLoginException.ABNORMAL_LIST.contains(loginId)) {
return;
}
SaManager.getSaTokenDao().delete(splicingKeyTokenValue(tokenValue));
// $$ 通知监听器
SaManager.getSaTokenListener().doLogout(loginType, loginId, tokenValue);
// 4. 尝试清理User-Session上的token签名 (如果为null或已被标记为异常, 那么无需继续执行 )
SaSession session = getSessionByLoginId(loginId, false);
if(session == null) {
return;
}
session.removeTokenSign(tokenValue);
// 5. 尝试注销User-Session
session.logoutByTokenSignCountToZero();
}
/**
* 会话注销根据账号id (踢人下线)
* <p> 当对方再次访问系统时会抛出NotLoginException异常场景值=-2
* @param loginId 账号id * @param loginId 账号id
*/ */
public void logoutByLoginId(Object loginId) { public void logout(Object loginId) {
logoutByLoginId(loginId, null); logout(loginId, null);
} }
/** /**
* 会话注销根据账号id and 设备标识 (踢人下线) * 会话注销根据账号id 设备标识
* <p> 当对方再次访问系统时会抛出NotLoginException异常场景值=-2 </p> *
* @param loginId 账号id * @param loginId 账号id
* @param device 设备标识 (填null代表所有注销设备) * @param device 设备标识 (填null代表所有注销设备)
*/ */
public void logoutByLoginId(Object loginId, String device) { public void logout(Object loginId, String device) {
// 1. 先获取这个账号的[user-session], 如果为null则不执行任何操作 clearTokenCommonMethod(loginId, device, tokenValue -> {
// 删除Token-Id映射 & 清除Token-Session
deleteTokenToIdMapping(tokenValue);
deleteTokenSession(tokenValue);
SaManager.getSaTokenListener().doLogout(loginType, loginId, tokenValue);
}, true);
}
/**
* 会话注销,根据指定 Token
*
* @param tokenValue 指定token
*/
public void logoutByTokenValue(String tokenValue) {
// 1. 清理 token-last-activity
clearLastActivity(tokenValue);
// 2. 注销 Token-Session
deleteTokenSession(tokenValue);
// if. 无效 loginId 立即返回
String loginId = getLoginIdNotHandle(tokenValue);
if(isValidLoginId(loginId) == false) {
if(loginId != null) {
deleteTokenToIdMapping(tokenValue);
}
return;
}
// 3. 清理token-id索引
deleteTokenToIdMapping(tokenValue);
// $$ 通知监听器某某Token注销下线了
SaManager.getSaTokenListener().doLogout(loginType, loginId, tokenValue);
// 4. 清理User-Session上的token签名 & 尝试注销User-Session
SaSession session = getSessionByLoginId(loginId, false);
if(session != null) {
session.removeTokenSign(tokenValue);
session.logoutByTokenSignCountToZero();
}
}
/**
* 踢人下线根据账号id
* <p> 当对方再次访问系统时会抛出NotLoginException异常场景值=-5 </p>
*
* @param loginId 账号id
*/
public void kickout(Object loginId) {
kickout(loginId, null);
}
/**
* 踢人下线根据账号id 和 设备标识
* <p> 当对方再次访问系统时会抛出NotLoginException异常场景值=-5 </p>
*
* @param loginId 账号id
* @param device 设备标识 (填null代表踢出所有设备)
*/
public void kickout(Object loginId, String device) {
clearTokenCommonMethod(loginId, device, tokenValue -> {
// 将此 token 标记为已被踢下线
updateTokenToIdMapping(tokenValue, NotLoginException.KICK_OUT);
SaManager.getSaTokenListener().doKickout(loginType, loginId, tokenValue);
}, true);
}
/**
* 踢人下线,根据指定 Token
* <p> 当对方再次访问系统时会抛出NotLoginException异常场景值=-5 </p>
*
* @param tokenValue 指定token
*/
public void kickoutByTokenValue(String tokenValue) {
// 1. 清理 token-last-activity
clearLastActivity(tokenValue);
// 2. 不注销 Token-Session
// if. 无效 loginId 立即返回
String loginId = getLoginIdNotHandle(tokenValue);
if(isValidLoginId(loginId) == false) {
return;
}
// 3. 给token打上标记被踢下线
updateTokenToIdMapping(tokenValue, NotLoginException.KICK_OUT);
// $$. 否则通知监听器某某Token被踢下线了
SaManager.getSaTokenListener().doKickout(loginType, loginId, tokenValue);
// 4. 清理User-Session上的token签名 & 尝试注销User-Session
SaSession session = getSessionByLoginId(loginId, false);
if(session != null) {
session.removeTokenSign(tokenValue);
session.logoutByTokenSignCountToZero();
}
}
/**
* 顶人下线根据账号id 和 设备标识
* <p> 当对方再次访问系统时会抛出NotLoginException异常场景值=-4 </p>
*
* @param loginId 账号id
* @param device 设备标识 (填null代表顶替所有设备)
*/
public void replaced(Object loginId, String device) {
clearTokenCommonMethod(loginId, device, tokenValue -> {
// 将此 token 标记为已被顶替
updateTokenToIdMapping(tokenValue, NotLoginException.BE_REPLACED);
SaManager.getSaTokenListener().doReplaced(loginType, loginId, tokenValue);
}, false);
}
/**
* 封装 注销、踢人、顶人 三个动作的相同代码无API含义方法
* @param loginId 账号id
* @param device 设备标识
* @param appendFun 追加操作
* @param isLogoutSession 是否注销 User-Session
*/
private void clearTokenCommonMethod(Object loginId, String device, Consumer<String> appendFun, boolean isLogoutSession) {
// 1. 如果此账号尚未登录,则不执行任何操作
SaSession session = getSessionByLoginId(loginId, false); SaSession session = getSessionByLoginId(loginId, false);
if(session == null) { if(session == null) {
return; return;
} }
// 2. 循环token签名列表开始删除相关信息 // 2. 循环token签名列表开始删除相关信息
List<TokenSign> tokenSignList = session.getTokenSignList(); for (TokenSign tokenSign : session.getTokenSignList()) {
for (TokenSign tokenSign : tokenSignList) {
if(device == null || tokenSign.getDevice().equals(device)) { if(device == null || tokenSign.getDevice().equals(device)) {
// 1. 获取token // -------- 共有操作
// s1. 获取token
String tokenValue = tokenSign.getValue(); String tokenValue = tokenSign.getValue();
// 2. 清理掉[token-最后操作时间] // s2. 清理掉[token-last-activity]
clearLastActivity(tokenValue); clearLastActivity(tokenValue);
// 3. 标记:已被踢下线 // s3. 从token签名列表移除
SaManager.getSaTokenDao().update(splicingKeyTokenValue(tokenValue), NotLoginException.KICK_OUT);
// 4. 清理账号session上的token签名
session.removeTokenSign(tokenValue); session.removeTokenSign(tokenValue);
// $$ 通知监听器 // -------- 追加操作
SaManager.getSaTokenListener().doLogoutByLoginId(loginType, loginId, tokenValue, tokenSign.getDevice()); appendFun.accept(tokenValue);
} }
} }
// 3. 尝试注销session // 3. 尝试注销session
session.logoutByTokenSignCountToZero(); if(isLogoutSession) {
session.logoutByTokenSignCountToZero();
}
} }
// 查询相关 // ---- 会话查询
/** /**
* 当前会话是否已经登录 * 当前会话是否已经登录
@@ -528,8 +607,44 @@ public class StpLogic {
return SaManager.getSaTokenDao().get(splicingKeyTokenValue(tokenValue)); return SaManager.getSaTokenDao().get(splicingKeyTokenValue(tokenValue));
} }
// ---- 其它操作
/**
* 判断一个 loginId 是否是有效的
* @param loginId 账号id
* @return /
*/
public boolean isValidLoginId(Object loginId) {
return loginId != null && !NotLoginException.ABNORMAL_LIST.contains(loginId.toString());
}
/**
* 删除 Token-Id 映射
* @param tokenValue token值
*/
public void deleteTokenToIdMapping(String tokenValue) {
SaManager.getSaTokenDao().delete(splicingKeyTokenValue(tokenValue));
}
/**
* 更改 Token 指向的 账号Id 值
* @param tokenValue token值
* @param loginId 新的账号Id值
*/
public void updateTokenToIdMapping(String tokenValue, Object loginId) {
SaTokenException.throwBy(SaFoxUtil.isEmpty(loginId), "LoginId 不能为空");
SaManager.getSaTokenDao().update(splicingKeyTokenValue(tokenValue), loginId.toString());
}
/**
* 存储 Token-Id 映射
* @param tokenValue token值
* @param loginId 账号id
* @param timeout 会话有效期 (单位: 秒)
*/
public void saveTokenToIdMapping(String tokenValue, Object loginId, long timeout) {
SaManager.getSaTokenDao().set(splicingKeyTokenValue(tokenValue), String.valueOf(loginId), timeout);
}
// ------------------- Session相关 -------------------
// ------------------- User-Session 相关 -------------------
/** /**
* 获取指定key的Session, 如果Session尚未创建isCreate=是否新建并返回 * 获取指定key的Session, 如果Session尚未创建isCreate=是否新建并返回
@@ -556,7 +671,7 @@ public class StpLogic {
} }
/** /**
* 获取指定账号id的Session, 如果Session尚未创建isCreate=是否新建并返回 * 获取指定账号id的User-Session, 如果Session尚未创建isCreate=是否新建并返回
* @param loginId 账号id * @param loginId 账号id
* @param isCreate 是否新建 * @param isCreate 是否新建
* @return Session对象 * @return Session对象
@@ -566,16 +681,16 @@ public class StpLogic {
} }
/** /**
* 获取指定账号id的Session如果Session尚未创建则新建并返回 * 获取指定账号id的User-Session如果Session尚未创建则新建并返回
* @param loginId 账号id * @param loginId 账号id
* @return Session对象 * @return Session对象
*/ */
public SaSession getSessionByLoginId(Object loginId) { public SaSession getSessionByLoginId(Object loginId) {
return getSessionByLoginId(loginId, true); return getSessionBySessionId(splicingKeySession(loginId), true);
} }
/** /**
* 获取当前会话的Session, 如果Session尚未创建isCreate=是否新建并返回 * 获取当前User-Session, 如果Session尚未创建isCreate=是否新建并返回
* @param isCreate 是否新建 * @param isCreate 是否新建
* @return Session对象 * @return Session对象
*/ */
@@ -584,7 +699,7 @@ public class StpLogic {
} }
/** /**
* 获取当前会话的Session如果Session尚未创建则新建并返回 * 获取当前User-Session如果Session尚未创建则新建并返回
* @return Session对象 * @return Session对象
*/ */
public SaSession getSession() { public SaSession getSession() {
@@ -592,7 +707,7 @@ public class StpLogic {
} }
// ------------------- token专属session ------------------- // ------------------- Token-Session 相关 -------------------
/** /**
* 获取指定Token-Session如果Session尚未创建isCreate代表是否新建并返回 * 获取指定Token-Session如果Session尚未创建isCreate代表是否新建并返回
@@ -635,7 +750,7 @@ public class StpLogic {
setTokenValue(tokenValue, cookieTimeout); setTokenValue(tokenValue, cookieTimeout);
} }
} }
// 返回这个token对应的专属session // 返回这个token对应的Token-Session
return getSessionBySessionId(splicingKeyTokenSession(getTokenValue()), isCreate); return getSessionBySessionId(splicingKeyTokenSession(getTokenValue()), isCreate);
} }
@@ -647,6 +762,13 @@ public class StpLogic {
return getTokenSession(true); return getTokenSession(true);
} }
/**
* 删除Token-Session
* @param tokenValue token值
*/
public void deleteTokenSession(String tokenValue) {
SaManager.getSaTokenDao().delete(splicingKeyTokenSession(tokenValue));
}
// ------------------- [临时过期] 验证相关 ------------------- // ------------------- [临时过期] 验证相关 -------------------
@@ -675,7 +797,7 @@ public class StpLogic {
// 删除[最后操作时间] // 删除[最后操作时间]
SaManager.getSaTokenDao().delete(splicingKeyLastActivityTime(tokenValue)); SaManager.getSaTokenDao().delete(splicingKeyLastActivityTime(tokenValue));
// 清除标记 // 清除标记
SaHolder.getStorage().delete((SaTokenConsts.TOKEN_ACTIVITY_TIMEOUT_CHECKED_KEY)); SaHolder.getStorage().delete(SaTokenConsts.TOKEN_ACTIVITY_TIMEOUT_CHECKED_KEY);
} }
/** /**
@@ -741,7 +863,7 @@ public class StpLogic {
// ------------------- 过期时间相关 ------------------- // ------------------- 过期时间相关 -------------------
/** /**
* 获取当前登录者的token剩余有效时间 (单位: 秒) * 获取当前登录者的 token 剩余有效时间 (单位: 秒)
* @return token剩余有效时间 * @return token剩余有效时间
*/ */
public long getTokenTimeout() { public long getTokenTimeout() {
@@ -749,7 +871,7 @@ public class StpLogic {
} }
/** /**
* 获取指定loginIdtoken剩余有效时间 (单位: 秒) * 获取指定 loginIdtoken 剩余有效时间 (单位: 秒)
* @param loginId 指定loginId * @param loginId 指定loginId
* @return token剩余有效时间 * @return token剩余有效时间
*/ */
@@ -758,7 +880,7 @@ public class StpLogic {
} }
/** /**
* 获取当前登录者的Session剩余有效时间 (单位: 秒) * 获取当前登录者的 User-Session 剩余有效时间 (单位: 秒)
* @return token剩余有效时间 * @return token剩余有效时间
*/ */
public long getSessionTimeout() { public long getSessionTimeout() {
@@ -766,7 +888,7 @@ public class StpLogic {
} }
/** /**
* 获取指定loginIdSession剩余有效时间 (单位: 秒) * 获取指定 loginId 的 User-Session 剩余有效时间 (单位: 秒)
* @param loginId 指定loginId * @param loginId 指定loginId
* @return token剩余有效时间 * @return token剩余有效时间
*/ */
@@ -775,7 +897,7 @@ public class StpLogic {
} }
/** /**
* 获取当前token的专属Session剩余有效时间 (单位: 秒) * 获取当前 Token-Session 剩余有效时间 (单位: 秒)
* @return token剩余有效时间 * @return token剩余有效时间
*/ */
public long getTokenSessionTimeout() { public long getTokenSessionTimeout() {
@@ -783,7 +905,7 @@ public class StpLogic {
} }
/** /**
* 获取指定token的专属Session剩余有效时间 (单位: 秒) * 获取指定 Token-Session 剩余有效时间 (单位: 秒)
* @param tokenValue 指定token * @param tokenValue 指定token
* @return token剩余有效时间 * @return token剩余有效时间
*/ */
@@ -792,15 +914,15 @@ public class StpLogic {
} }
/** /**
* 获取当前token[临时过期]剩余有效时间 (单位: 秒) * 获取当前 token [临时过期] 剩余有效时间 (单位: 秒)
* @return token[临时过期]剩余有效时间 * @return token [临时过期] 剩余有效时间
*/ */
public long getTokenActivityTimeout() { public long getTokenActivityTimeout() {
return getTokenActivityTimeoutByToken(getTokenValue()); return getTokenActivityTimeoutByToken(getTokenValue());
} }
/** /**
* 获取指定token[临时过期]剩余有效时间 (单位: 秒) * 获取指定 token [临时过期] 剩余有效时间 (单位: 秒)
* @param tokenValue 指定token * @param tokenValue 指定token
* @return token[临时过期]剩余有效时间 * @return token[临时过期]剩余有效时间
*/ */
@@ -963,6 +1085,8 @@ public class StpLogic {
} }
// ------------------- id 反查token 相关操作 ------------------- // ------------------- id 反查token 相关操作 -------------------
/** /**
@@ -1026,7 +1150,7 @@ public class StpLogic {
* @return 当前令牌的登录设备 * @return 当前令牌的登录设备
*/ */
public String getLoginDevice() { public String getLoginDevice() {
// 如果没有token直接返回 // 如果没有token直接返回 null
String tokenValue = getTokenValue(); String tokenValue = getTokenValue();
if(tokenValue == null) { if(tokenValue == null) {
return null; return null;
@@ -1364,4 +1488,30 @@ public class StpLogic {
} }
// ------------------- 历史API兼容旧版本 -------------------
/**
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.kickout() ,使用方式保持不变 </h1>
*
* 会话注销根据账号id (踢人下线)
* <p> 当对方再次访问系统时会抛出NotLoginException异常场景值=-2
* @param loginId 账号id
*/
public void logoutByLoginId(Object loginId) {
this.kickout(loginId);
}
/**
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.kickout() ,使用方式保持不变 </h1>
*
* 会话注销根据账号id and 设备标识 (踢人下线)
* <p> 当对方再次访问系统时会抛出NotLoginException异常场景值=-2 </p>
* @param loginId 账号id
* @param device 设备标识 (填null代表所有注销设备)
*/
public void logoutByLoginId(Object loginId, String device) {
this.kickout(loginId, device);
}
} }

View File

@@ -6,7 +6,7 @@ import cn.dev33.satoken.fun.SaFunction;
import cn.dev33.satoken.session.SaSession; import cn.dev33.satoken.session.SaSession;
/** /**
* Sa-Token 权限证工具类 * Sa-Token 权限证工具类
* @author kong * @author kong
*/ */
public class StpUtil { public class StpUtil {
@@ -68,6 +68,8 @@ public class StpUtil {
// =================== 登录相关操作 =================== // =================== 登录相关操作 ===================
// --- 登录
/** /**
* 会话登录 * 会话登录
* @param id 账号id建议的类型long | int | String * @param id 账号id建议的类型long | int | String
@@ -103,6 +105,8 @@ public class StpUtil {
stpLogic.login(id, loginModel); stpLogic.login(id, loginModel);
} }
// --- 注销
/** /**
* 会话注销 * 会话注销
*/ */
@@ -111,7 +115,26 @@ public class StpUtil {
} }
/** /**
* 会话注销,根据指定Token * 会话注销,根据账号id
* @param loginId 账号id
*/
public static void logout(Object loginId) {
stpLogic.logout(loginId);
}
/**
* 会话注销根据账号id 和 设备标识
*
* @param loginId 账号id
* @param device 设备标识 (填null代表所有注销设备)
*/
public static void logout(Object loginId, String device) {
stpLogic.logout(loginId, device);
}
/**
* 会话注销,根据指定 Token
*
* @param tokenValue 指定token * @param tokenValue 指定token
*/ */
public static void logoutByTokenValue(String tokenValue) { public static void logoutByTokenValue(String tokenValue) {
@@ -119,24 +142,48 @@ public class StpUtil {
} }
/** /**
* 会话注销根据账号id (踢人下线) * 踢人下线根据账号id
* <p> 当对方再次访问系统时会抛出NotLoginException异常场景值=-2 </p> * <p> 当对方再次访问系统时会抛出NotLoginException异常场景值=-5 </p>
*
* @param loginId 账号id * @param loginId 账号id
*/ */
public static void logoutByLoginId(Object loginId) { public static void kickout(Object loginId) {
stpLogic.logoutByLoginId(loginId); stpLogic.kickout(loginId);
} }
/** /**
* 会话注销根据账号id and 设备标识 (踢人下线) * 踢人下线根据账号id 设备标识
* <p> 当对方再次访问系统时会抛出NotLoginException异常场景值=-2 * <p> 当对方再次访问系统时会抛出NotLoginException异常场景值=-5 </p>
*
* @param loginId 账号id * @param loginId 账号id
* @param device 设备标识 * @param device 设备标识 (填null代表踢出所有设备)
*/ */
public static void logoutByLoginId(Object loginId, String device) { public static void kickout(Object loginId, String device) {
stpLogic.logoutByLoginId(loginId, device); stpLogic.kickout(loginId, device);
} }
/**
* 踢人下线,根据指定 Token
* <p> 当对方再次访问系统时会抛出NotLoginException异常场景值=-5 </p>
*
* @param tokenValue 指定token
*/
public static void kickoutByTokenValue(String tokenValue) {
stpLogic.kickoutByTokenValue(tokenValue);
}
/**
* 顶人下线根据账号id 和 设备标识
* <p> 当对方再次访问系统时会抛出NotLoginException异常场景值=-4 </p>
*
* @param loginId 账号id
* @param device 设备标识 (填null代表顶替所有设备)
*/
public static void replaced(Object loginId, String device) {
stpLogic.replaced(loginId, device);
}
// 查询相关 // 查询相关
/** /**
@@ -214,7 +261,7 @@ public class StpUtil {
} }
// =================== session相关 =================== // =================== User-Session 相关 ===================
/** /**
* 获取指定账号id的Session, 如果Session尚未创建isCreate=是否新建并返回 * 获取指定账号id的Session, 如果Session尚未创建isCreate=是否新建并返回
@@ -262,7 +309,7 @@ public class StpUtil {
} }
// =================== token专属session =================== // =================== Token-Session 相关 ===================
/** /**
* 获取指定Token-Session如果Session尚未创建则新建并返回 * 获取指定Token-Session如果Session尚未创建则新建并返回
@@ -304,7 +351,7 @@ public class StpUtil {
// =================== 过期时间相关 =================== // =================== 过期时间相关 ===================
/** /**
* 获取当前登录者的token剩余有效时间 (单位: 秒) * 获取当前登录者的 token 剩余有效时间 (单位: 秒)
* @return token剩余有效时间 * @return token剩余有效时间
*/ */
public static long getTokenTimeout() { public static long getTokenTimeout() {
@@ -312,7 +359,7 @@ public class StpUtil {
} }
/** /**
* 获取当前登录者的Session剩余有效时间 (单位: 秒) * 获取当前登录者的 User-Session 剩余有效时间 (单位: 秒)
* @return token剩余有效时间 * @return token剩余有效时间
*/ */
public static long getSessionTimeout() { public static long getSessionTimeout() {
@@ -320,7 +367,7 @@ public class StpUtil {
} }
/** /**
* 获取当前token的专属Session剩余有效时间 (单位: 秒) * 获取当前 Token-Session 剩余有效时间 (单位: 秒)
* @return token剩余有效时间 * @return token剩余有效时间
*/ */
public static long getTokenSessionTimeout() { public static long getTokenSessionTimeout() {
@@ -328,8 +375,8 @@ public class StpUtil {
} }
/** /**
* 获取当前token[临时过期]剩余有效时间 (单位: 秒) * 获取当前 token [临时过期] 剩余有效时间 (单位: 秒)
* @return token[临时过期]剩余有效时间 * @return token [临时过期] 剩余有效时间
*/ */
public static long getTokenActivityTimeout() { public static long getTokenActivityTimeout() {
return stpLogic.getTokenActivityTimeout(); return stpLogic.getTokenActivityTimeout();
@@ -637,6 +684,7 @@ public class StpUtil {
/** /**
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.getLoginType() ,使用方式保持不变 </h1> * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.getLoginType() ,使用方式保持不变 </h1>
*
* 获取当前StpLogin的loginKey * 获取当前StpLogin的loginKey
* @return 当前StpLogin的loginKey * @return 当前StpLogin的loginKey
*/ */
@@ -647,6 +695,7 @@ public class StpUtil {
/** /**
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1> * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
*
* 在当前会话上登录id * 在当前会话上登录id
* @param loginId 登录id建议的类型long | int | String * @param loginId 登录id建议的类型long | int | String
*/ */
@@ -657,6 +706,7 @@ public class StpUtil {
/** /**
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1> * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
*
* 在当前会话上登录id, 并指定登录设备 * 在当前会话上登录id, 并指定登录设备
* @param loginId 登录id建议的类型long | int | String * @param loginId 登录id建议的类型long | int | String
* @param device 设备标识 * @param device 设备标识
@@ -668,6 +718,7 @@ public class StpUtil {
/** /**
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1> * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
*
* 在当前会话上登录id, 并指定登录设备 * 在当前会话上登录id, 并指定登录设备
* @param loginId 登录id建议的类型long | int | String * @param loginId 登录id建议的类型long | int | String
* @param isLastingCookie 是否为持久Cookie * @param isLastingCookie 是否为持久Cookie
@@ -679,6 +730,7 @@ public class StpUtil {
/** /**
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1> * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
*
* 在当前会话上登录id, 并指定所有登录参数Model * 在当前会话上登录id, 并指定所有登录参数Model
* @param loginId 登录id建议的类型long | int | String * @param loginId 登录id建议的类型long | int | String
* @param loginModel 此次登录的参数Model * @param loginModel 此次登录的参数Model
@@ -688,4 +740,27 @@ public class StpUtil {
stpLogic.login(loginId, loginModel); stpLogic.login(loginId, loginModel);
} }
/**
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.kickout() ,使用方式保持不变 </h1>
*
* 会话注销根据账号id (踢人下线)
* <p> 当对方再次访问系统时会抛出NotLoginException异常场景值=-2
* @param loginId 账号id
*/
public static void logoutByLoginId(Object loginId) {
stpLogic.kickout(loginId);
}
/**
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.kickout() ,使用方式保持不变 </h1>
*
* 会话注销根据账号id and 设备标识 (踢人下线)
* <p> 当对方再次访问系统时会抛出NotLoginException异常场景值=-2 </p>
* @param loginId 账号id
* @param device 设备标识 (填null代表所有注销设备)
*/
public static void logoutByLoginId(Object loginId, String device) {
stpLogic.kickout(loginId, device);
}
} }

View File

@@ -185,7 +185,7 @@ public class TestController {
// 先登录上 // 先登录上
StpUtil.login(10001); StpUtil.login(10001);
// 踢下线 // 踢下线
StpUtil.logoutByLoginId(10001); StpUtil.kickout(10001);
// 再尝试获取 // 再尝试获取
StpUtil.getLoginId(); StpUtil.getLoginId();
// 返回 // 返回

View File

@@ -2,8 +2,6 @@ package com.pj.satoken.at;
import java.util.List; import java.util.List;
import org.springframework.stereotype.Component;
import cn.dev33.satoken.fun.SaFunction; import cn.dev33.satoken.fun.SaFunction;
import cn.dev33.satoken.session.SaSession; import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.SaLoginModel; import cn.dev33.satoken.stp.SaLoginModel;
@@ -11,10 +9,9 @@ import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpLogic; import cn.dev33.satoken.stp.StpLogic;
/** /**
* Sa-Token 权限证工具类 (User版) * Sa-Token 权限证工具类
* @author kong * @author kong
*/ */
@Component
public class StpUserUtil { public class StpUserUtil {
/** /**
@@ -74,6 +71,8 @@ public class StpUserUtil {
// =================== 登录相关操作 =================== // =================== 登录相关操作 ===================
// --- 登录
/** /**
* 会话登录 * 会话登录
* @param id 账号id建议的类型long | int | String * @param id 账号id建议的类型long | int | String
@@ -109,6 +108,8 @@ public class StpUserUtil {
stpLogic.login(id, loginModel); stpLogic.login(id, loginModel);
} }
// --- 注销
/** /**
* 会话注销 * 会话注销
*/ */
@@ -117,7 +118,26 @@ public class StpUserUtil {
} }
/** /**
* 会话注销,根据指定Token * 会话注销,根据账号id
* @param loginId 账号id
*/
public static void logout(Object loginId) {
stpLogic.logout(loginId);
}
/**
* 会话注销根据账号id 和 设备标识
*
* @param loginId 账号id
* @param device 设备标识 (填null代表所有注销设备)
*/
public static void logout(Object loginId, String device) {
stpLogic.logout(loginId, device);
}
/**
* 注销会话,根据指定 Token
*
* @param tokenValue 指定token * @param tokenValue 指定token
*/ */
public static void logoutByTokenValue(String tokenValue) { public static void logoutByTokenValue(String tokenValue) {
@@ -125,24 +145,48 @@ public class StpUserUtil {
} }
/** /**
* 会话注销根据账号id (踢人下线) * 踢人下线根据账号id
* <p> 当对方再次访问系统时会抛出NotLoginException异常场景值=-2 * <p> 当对方再次访问系统时会抛出NotLoginException异常场景值=-5 </p>
*
* @param loginId 账号id * @param loginId 账号id
*/ */
public static void logoutByLoginId(Object loginId) { public static void kickout(Object loginId) {
stpLogic.logoutByLoginId(loginId); stpLogic.kickout(loginId);
} }
/** /**
* 会话注销根据账号id & 设备标识 (踢人下线) * 踢人下线根据账号id 设备标识
* <p> 当对方再次访问系统时会抛出NotLoginException异常场景值=-2 * <p> 当对方再次访问系统时会抛出NotLoginException异常场景值=-5 </p>
*
* @param loginId 账号id * @param loginId 账号id
* @param device 设备标识 * @param device 设备标识 (填null代表踢出所有设备)
*/ */
public static void logoutByLoginId(Object loginId, String device) { public static void kickout(Object loginId, String device) {
stpLogic.logoutByLoginId(loginId, device); stpLogic.kickout(loginId, device);
} }
/**
* 踢人下线,根据指定 Token
* <p> 当对方再次访问系统时会抛出NotLoginException异常场景值=-5 </p>
*
* @param tokenValue 指定token
*/
public static void kickoutByTokenValue(String tokenValue) {
stpLogic.kickoutByTokenValue(tokenValue);
}
/**
* 顶人下线根据账号id 和 设备标识
* <p> 当对方再次访问系统时会抛出NotLoginException异常场景值=-4 </p>
*
* @param loginId 账号id
* @param device 设备标识 (填null代表顶替所有设备)
*/
public static void replaced(Object loginId, String device) {
stpLogic.replaced(loginId, device);
}
// 查询相关 // 查询相关
/** /**
@@ -602,7 +646,7 @@ public class StpUserUtil {
/** /**
* 在当前会话 开启二级认证 * 在当前会话 开启二级认证
* @param timeout 维持时间 (单位: 秒) * @param safeTime 维持时间 (单位: 秒)
*/ */
public static void openSafe(long safeTime) { public static void openSafe(long safeTime) {
stpLogic.openSafe(safeTime); stpLogic.openSafe(safeTime);
@@ -625,7 +669,7 @@ public class StpUserUtil {
/** /**
* 获取当前会话的二级认证剩余有效时间 (单位: 秒, 返回-2代表尚未通过二级认证) * 获取当前会话的二级认证剩余有效时间 (单位: 秒, 返回-2代表尚未通过二级认证)
* @return * @return 剩余有效时间
*/ */
public static long getSafeTime() { public static long getSafeTime() {
return stpLogic.getSafeTime(); return stpLogic.getSafeTime();

View File

@@ -186,7 +186,7 @@ public class TestController {
// 先登录上 // 先登录上
StpUtil.login(10001); StpUtil.login(10001);
// 踢下线 // 踢下线
StpUtil.logoutByLoginId(10001); StpUtil.kickout(10001);
// 再尝试获取 // 再尝试获取
StpUtil.getLoginId(); StpUtil.getLoginId();
// 返回 // 返回
@@ -240,6 +240,7 @@ public class TestController {
@RequestMapping("test") @RequestMapping("test")
public AjaxJson test() { public AjaxJson test() {
System.out.println("进来了"); System.out.println("进来了");
StpUtil.checkLogin();
return AjaxJson.getSuccess(); return AjaxJson.getSuccess();
} }

View File

@@ -92,7 +92,7 @@ public class SaTokenSpringBootStarterTest {
// 根据账号id踢人 // 根据账号id踢人
StpUtil.login(10001); StpUtil.login(10001);
Assert.assertTrue(StpUtil.isLogin()); Assert.assertTrue(StpUtil.isLogin());
StpUtil.logoutByLoginId(10001); StpUtil.kickout(10001);
Assert.assertFalse(StpUtil.isLogin()); Assert.assertFalse(StpUtil.isLogin());
} }