diff --git a/README.md b/README.md index c30805e6..3e5e92db 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@

logo

-

sa-token v1.8.0

+

sa-token v1.9.0

一个JavaWeb轻量级权限认证框架,功能全面,上手简单

- + @@ -16,7 +16,7 @@ --- -## 😘 在线资料 +## 在线资料 - [官网首页:http://sa-token.dev33.cn/](http://sa-token.dev33.cn/) @@ -27,8 +27,8 @@ - [开源不易,求鼓励,点个star吧](https://github.com/click33/sa-token) -## ⭐ sa-token是什么? -**sa-token是一个JavaWeb轻量级权限认证框架,其API调用非常简单,有多简单呢?以登录验证为例,你只需要:** +## sa-token是什么? +sa-token是一个JavaWeb轻量级权限认证框架,其API调用非常简单,有多简单呢?以登录验证为例,你只需要: ``` java // 在登录时写入当前会话的账号id @@ -39,16 +39,16 @@ StpUtil.checkLogin(); ``` -**没有复杂的封装!不要任何的配置!只需这两行简单的调用,即可轻松完成系统登录鉴权!** +没有复杂的封装!不要任何的配置!只需这两行简单的调用,即可轻松完成系统登录鉴权! -## 🔥 框架设计思想 +## 框架设计思想 与其它权限认证框架相比,`sa-token`尽力保证两点: - 上手简单:能自动化的配置全部自动化,不让你费脑子 - 功能强大:能涵盖的功能全部涵盖,不让你用个框架还要自己给框架打各种补丁 -**如果上面的示例能够证明`sa-token`的简单,那么以下API则可以证明`sa-token`的强大** +如果上面的示例能够证明`sa-token`的简单,那么以下API则可以证明`sa-token`的强大 ``` java StpUtil.setLoginId(10001); // 标记当前会话登录的账号id StpUtil.getLoginId(); // 获取当前会话登录的账号id @@ -60,13 +60,15 @@ StpUtil.hasPermission("user:add"); // 查询当前账号是否含有指定权 StpUtil.getSession(); // 获取当前账号id的Session StpUtil.getSessionByLoginId(10001); // 获取账号id为10001的Session StpUtil.getTokenValueByLoginId(10001); // 获取账号id为10001的token令牌值 +StpUtil.setLoginId(10001, "PC"); // 指定设备标识登录 +StpUtil.logoutByLoginId(10001, "PC"); // 指定设备标识进行强制注销 (不同端不受影响) ``` -**sa-token的API众多,请恕此处无法为您逐一展示,更多示例请戳官方在线文档** +sa-token的API众多,请恕此处无法为您逐一展示,更多示例请戳官方在线文档 -## 💦️️ 涵盖功能 +## 涵盖功能 - **登录验证** —— 轻松登录鉴权,并提供五种细分场景值 - **权限验证** —— 拦截违规调用,不同角色不同授权 - **Session会话** —— 专业的数据缓存中心 @@ -78,11 +80,12 @@ StpUtil.getTokenValueByLoginId(10001); // 获取账号id为10001的token令牌 - **注解式鉴权** —— 优雅的将鉴权与业务代码分离 - **花式token生成** —— 内置六种token风格,还可自定义token生成策略 - **自动续签** —— 提供两种token过期策略,灵活搭配使用,还可自动续签 +- **同端互斥登录** —— 像QQ一样手机电脑同时在线,但是两个手机上互斥登录 - **组件自动注入** —— 零配置与Spring等框架集成 - **更多功能正在集成中...** —— 如有您有好想法或者建议,欢迎加群交流 -## 🔨 贡献代码 +## 贡献代码 sa-token欢迎大家贡献代码,为框架添砖加瓦 1. 在github上fork一份到自己的仓库 2. clone自己的仓库到本地电脑 @@ -91,7 +94,7 @@ sa-token欢迎大家贡献代码,为框架添砖加瓦 5. 等待合并 -## 🌱 建议贡献的地方 +## 建议贡献的地方 - 修复源码现有bug,或增加新的实用功能 - 完善在线文档,或者修复现有错误之处 - 更多demo示例:比如SSM版搭建步骤 @@ -99,11 +102,11 @@ sa-token欢迎大家贡献代码,为框架添砖加瓦 - 如果更新实用功能,可在文档友情链接处留下自己的推广链接 -## 🚀 友情链接 +## 友情链接 [**[ okhttps ]** 一个轻量级http通信框架,支持 WebSocket 以及 Stomp 协议](https://gitee.com/ejlchina-zhxu/okhttps) -## 😎 交流群 +## 交流群 QQ交流群:[1002350610 点击加入](https://jq.qq.com/?_wv=1027&k=45H977HM) ,欢迎你的加入 diff --git a/pom.xml b/pom.xml index d2a16569..1e595910 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ cn.dev33 sa-token-parent pom - 1.8.0 + 1.9.0 sa-token diff --git a/sa-token-core/pom.xml b/sa-token-core/pom.xml index 3baf598e..ad96cd6f 100644 --- a/sa-token-core/pom.xml +++ b/sa-token-core/pom.xml @@ -7,7 +7,7 @@ cn.dev33 sa-token-parent - 1.8.0 + 1.9.0 jar diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/stp/SaTokenInfo.java b/sa-token-core/src/main/java/cn/dev33/satoken/stp/SaTokenInfo.java index a6c7ada7..66a8ec13 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/stp/SaTokenInfo.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/stp/SaTokenInfo.java @@ -31,18 +31,18 @@ public class SaTokenInfo { /** token专属session剩余有效时间 (单位: 秒) */ public long tokenSessionTimeout; - - - - /** * token剩余无操作有效时间 */ public long tokenActivityTimeout; - + /** 当前登录设备 */ + public String loginDevice; + + + /** - * @return tokenName token名称 + * @return tokenName */ public String getTokenName() { return tokenName; @@ -50,11 +50,9 @@ public class SaTokenInfo { /** * @param tokenName 要设置的 tokenName - * @return 对象自身 */ - public SaTokenInfo setTokenName(String tokenName) { + public void setTokenName(String tokenName) { this.tokenName = tokenName; - return this; } /** @@ -66,11 +64,9 @@ public class SaTokenInfo { /** * @param tokenValue 要设置的 tokenValue - * @return 对象自身 */ - public SaTokenInfo setTokenValue(String tokenValue) { + public void setTokenValue(String tokenValue) { this.tokenValue = tokenValue; - return this; } /** @@ -82,11 +78,9 @@ public class SaTokenInfo { /** * @param isLogin 要设置的 isLogin - * @return 对象自身 */ - public SaTokenInfo setIsLogin(Boolean isLogin) { + public void setIsLogin(Boolean isLogin) { this.isLogin = isLogin; - return this; } /** @@ -98,11 +92,9 @@ public class SaTokenInfo { /** * @param loginId 要设置的 loginId - * @return 对象自身 */ - public SaTokenInfo setLoginId(Object loginId) { + public void setLoginId(Object loginId) { this.loginId = loginId; - return this; } /** @@ -114,13 +106,11 @@ public class SaTokenInfo { /** * @param loginKey 要设置的 loginKey - * @return 对象自身 */ - public SaTokenInfo setLoginKey(String loginKey) { + public void setLoginKey(String loginKey) { this.loginKey = loginKey; - return this; } - + /** * @return tokenTimeout */ @@ -130,11 +120,9 @@ public class SaTokenInfo { /** * @param tokenTimeout 要设置的 tokenTimeout - * @return 对象自身 */ - public SaTokenInfo setTokenTimeout(long tokenTimeout) { + public void setTokenTimeout(long tokenTimeout) { this.tokenTimeout = tokenTimeout; - return this; } /** @@ -146,11 +134,9 @@ public class SaTokenInfo { /** * @param sessionTimeout 要设置的 sessionTimeout - * @return 对象自身 */ - public SaTokenInfo setSessionTimeout(long sessionTimeout) { + public void setSessionTimeout(long sessionTimeout) { this.sessionTimeout = sessionTimeout; - return this; } /** @@ -162,13 +148,11 @@ public class SaTokenInfo { /** * @param tokenSessionTimeout 要设置的 tokenSessionTimeout - * @return 对象自身 */ - public SaTokenInfo setTokenSessionTimeout(long tokenSessionTimeout) { + public void setTokenSessionTimeout(long tokenSessionTimeout) { this.tokenSessionTimeout = tokenSessionTimeout; - return this; } - + /** * @return tokenActivityTimeout */ @@ -178,11 +162,23 @@ public class SaTokenInfo { /** * @param tokenActivityTimeout 要设置的 tokenActivityTimeout - * @return 对象自身 */ - public SaTokenInfo setTokenActivityTimeout(long tokenActivityTimeout) { + public void setTokenActivityTimeout(long tokenActivityTimeout) { this.tokenActivityTimeout = tokenActivityTimeout; - return this; + } + + /** + * @return loginDevice + */ + public String getLoginDevice() { + return loginDevice; + } + + /** + * @param loginDevice 要设置的 loginDevice + */ + public void setLoginDevice(String loginDevice) { + this.loginDevice = loginDevice; } @@ -193,8 +189,9 @@ public class SaTokenInfo { return "SaTokenInfo [tokenName=" + tokenName + ", tokenValue=" + tokenValue + ", isLogin=" + isLogin + ", loginId=" + loginId + ", loginKey=" + loginKey + ", tokenTimeout=" + tokenTimeout + ", sessionTimeout=" + sessionTimeout + ", tokenSessionTimeout=" + tokenSessionTimeout - + ", tokenActivityTimeout=" + tokenActivityTimeout + "]"; + + ", tokenActivityTimeout=" + tokenActivityTimeout + ", loginDevice=" + loginDevice + "]"; } + @@ -208,6 +205,4 @@ public class SaTokenInfo { - - } diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpLogic.java b/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpLogic.java index 4da0bfff..c8060c57 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpLogic.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpLogic.java @@ -119,6 +119,7 @@ public class StpLogic { info.sessionTimeout = getSessionTimeout(); info.tokenSessionTimeout = getTokenSessionTimeout(); info.tokenActivityTimeout = getTokenActivityTimeout(); + info.loginDevice = getLoginDevice(); return info; } @@ -130,11 +131,23 @@ public class StpLogic { * @param loginId 登录id,建议的类型:(long | int | String) */ public void setLoginId(Object loginId) { + setLoginId(loginId, SaTokenConsts.DEFAULT_LOGIN_DEVICE); + } + + /** + * 在当前会话上登录id + * @param loginId 登录id,建议的类型:(long | int | String) + * @param device 设备标识 + */ + public void setLoginId(Object loginId, String device) { - // ------ 0、如果当前会话已经登录上了此LoginId,则立即返回 + // ------ 0、如果当前会话已经登录上了此LoginId,且登录设备相同,则立即返回 Object loggedId = getLoginIdDefaultNull(); if(loggedId != null && loggedId.toString().equals(loginId.toString())) { - return; + String loggedDevice = getLoginDevice(); + if(loggedDevice != null && loggedDevice.equals(device)) { + return; + } } // ------ 1、获取相应对象 @@ -148,18 +161,20 @@ public class StpLogic { if(config.getAllowConcurrentLogin() == true) { // 如果配置为共享token, 则尝试从session签名记录里取出token if(config.getIsShare() == true) { - tokenValue = getTokenValueByLoginId(loginId); + tokenValue = getTokenValueByLoginId(loginId, device); } } else { // --- 如果不允许并发登录 - // 如果此时[id-session]不为null,说明此账号在其他地正在登录,现在需要先把其它地的token标记为被顶下线 + // 如果此时[id-session]不为null,说明此账号在其他地正在登录,现在需要先把其它地的同设备token标记为被顶下线 SaSession session = getSessionByLoginId(loginId, false); if(session != null) { - List tokenValueList = getTokenValueListByLoginId(loginId); - for (String token : tokenValueList) { - dao.updateValue(getKeyTokenValue(token), NotLoginException.BE_REPLACED); // 1. 将此token 标记为已顶替 - clearLastActivity(token); // 2. 清理掉[token-最后操作时间] - session.removeTokenSign(token); // 3. 清理账号session上的token签名 + List tokenSignList = session.getTokenSignList(); + for (TokenSign tokenSign : tokenSignList) { + if(tokenSign.getDevice().equals(device)) { + dao.updateValue(getKeyTokenValue(tokenSign.getValue()), NotLoginException.BE_REPLACED); // 1. 将此token 标记为已顶替 + clearLastActivity(tokenSign.getValue()); // 2. 清理掉[token-最后操作时间] + session.removeTokenSign(tokenSign.getValue()); // 3. 清理账号session上的token签名记录 + } } } } @@ -176,7 +191,7 @@ public class StpLogic { dao.updateSessionTimeout(session.getId(), config.getTimeout()); } // 在session上记录token签名 - session.addTokenSign(new TokenSign(tokenValue, SaTokenConsts.DEFAULT_LOGIN_DEVICE)); + session.addTokenSign(new TokenSign(tokenValue, device)); // ------ 4. 持久化其它数据 dao.setValue(getKeyTokenValue(tokenValue), String.valueOf(loginId), config.getTimeout()); // token -> uid @@ -187,6 +202,7 @@ public class StpLogic { } } + /** * 当前会话注销登录 */ @@ -203,6 +219,7 @@ public class StpLogic { logoutByTokenValue(tokenValue); } + /** * 指定token的会话注销登录 * @param tokenValue 指定token @@ -229,12 +246,23 @@ public class StpLogic { session.logoutByTokenSignCountToZero(); } + /** * 指定loginId的会话注销登录(踢人下线) *

当对方再次访问系统时,会抛出NotLoginException异常,场景值=-2 * @param loginId 账号id */ public void logoutByLoginId(Object loginId) { + logoutByLoginId(loginId, null); + } + + /** + * 指定loginId指定设备的会话注销登录(踢人下线) + *

当对方再次访问系统时,会抛出NotLoginException异常,场景值=-2 + * @param loginId 账号id + * @param device 设备标识 (填null代表所有注销设备) + */ + public void logoutByLoginId(Object loginId, String device) { // 先获取这个账号的[id-session], 如果为null,则不执行任何操作 SaSession session = getSessionByLoginId(loginId); if(session == null) { @@ -244,14 +272,16 @@ public class StpLogic { // 循环token签名列表,开始删除相关信息 List tokenSignList = session.getTokenSignList(); for (TokenSign tokenSign : tokenSignList) { - // 1. 获取token - String tokenValue = tokenSign.getValue(); - // 2. 清理掉[token-最后操作时间] - clearLastActivity(tokenValue); - // 3. 标记:已被踢下线 - SaTokenManager.getSaTokenDao().updateValue(getKeyTokenValue(tokenValue), NotLoginException.KICK_OUT); // 标记:已被踢下线 - // 4. 清理账号session上的token签名 - session.removeTokenSign(tokenValue); + if(device == null || tokenSign.getDevice().equals(device)) { + // 1. 获取token + String tokenValue = tokenSign.getValue(); + // 2. 清理掉[token-最后操作时间] + clearLastActivity(tokenValue); + // 3. 标记:已被踢下线 + SaTokenManager.getSaTokenDao().updateValue(getKeyTokenValue(tokenValue), NotLoginException.KICK_OUT); // 标记:已被踢下线 + // 4. 清理账号session上的token签名 + session.removeTokenSign(tokenValue); + } } // 尝试注销session session.logoutByTokenSignCountToZero(); @@ -269,6 +299,7 @@ public class StpLogic { return getLoginIdDefaultNull() != null; } + /** * 检验当前会话是否已经登录,如未登录,则抛出异常 */ @@ -276,6 +307,7 @@ public class StpLogic { getLoginId(); } + /** * 获取当前会话账号id, 如果未登录,则抛出异常 * @return 账号id @@ -310,7 +342,8 @@ public class StpLogic { return loginId; } - /** + + /** * 获取当前会话登录id, 如果未登录,则返回默认值 * @param 返回类型 * @param defaultValue 默认值 @@ -336,7 +369,8 @@ public class StpLogic { return (T)loginId; } - /** + + /** * 获取当前会话登录id, 如果未登录,则返回null * @return 账号id */ @@ -359,6 +393,7 @@ public class StpLogic { return loginId; } + /** * 获取当前会话登录id, 并转换为String * @return 账号id @@ -367,7 +402,8 @@ public class StpLogic { return String.valueOf(getLoginId()); } - /** + + /** * 获取当前会话登录id, 并转换为int * @return 账号id */ @@ -379,7 +415,8 @@ public class StpLogic { return Integer.valueOf(String.valueOf(getLoginId())); } - /** + + /** * 获取当前会话登录id, 并转换为long * @return 账号id */ @@ -391,6 +428,7 @@ public class StpLogic { return Long.valueOf(String.valueOf(getLoginId())); } + /** * 获取指定token对应的登录id,如果未登录,则返回 null * @param tokenValue token @@ -424,6 +462,7 @@ public class StpLogic { return session; } + /** * 获取指定loginId的session, 如果session尚未创建,isCreate=是否新建并返回 * @param loginId 账号id @@ -434,6 +473,7 @@ public class StpLogic { return getSessionBySessionId(getKeySession(loginId), isCreate); } + /** * 获取指定loginId的session,如果session尚未创建,则新建并返回 * @param loginId 账号id @@ -443,6 +483,7 @@ public class StpLogic { return getSessionByLoginId(loginId, true); } + /** * 获取当前会话的session, 如果session尚未创建,isCreate=是否新建并返回 * @param isCreate 是否新建 @@ -452,6 +493,7 @@ public class StpLogic { return getSessionByLoginId(getLoginId(), isCreate); } + /** * 获取当前会话的session,如果session尚未创建,则新建并返回 * @return 当前会话的session @@ -463,6 +505,7 @@ public class StpLogic { // =================== token专属session =================== + /** * 获取指定token的专属session,如果session尚未创建,isCreate代表是否新建并返回 * @param tokenValue token值 @@ -473,6 +516,7 @@ public class StpLogic { return getSessionBySessionId(getKeyTokenSession(tokenValue), isCreate); } + /** * 获取指定token的专属session,如果session尚未创建,则新建并返回 * @param tokenValue token值 @@ -482,6 +526,7 @@ public class StpLogic { return getSessionBySessionId(getKeyTokenSession(tokenValue), true); } + /** * 获取当前token的专属-session,如果session尚未创建,isCreate代表是否新建并返回 * @param isCreate 是否新建 @@ -502,6 +547,7 @@ public class StpLogic { return getSessionBySessionId(getKeyTokenSession(getTokenValue()), isCreate); } + /** * 获取当前token的专属-session,如果session尚未创建,则新建并返回 * @return session会话 @@ -513,7 +559,8 @@ public class StpLogic { // =================== [临时过期] 验证相关 =================== - /** + + /** * 写入指定token的 [最后操作时间] 为当前时间戳 * @param tokenValue 指定token */ @@ -526,6 +573,7 @@ public class StpLogic { SaTokenManager.getSaTokenDao().setValue(getKeyLastActivityTime(tokenValue), String.valueOf(System.currentTimeMillis()), getConfig().getTimeout()); } + /** * 清除指定token的 [最后操作时间] * @param tokenValue 指定token @@ -541,6 +589,7 @@ public class StpLogic { SaTokenManager.getSaTokenServlet().getRequest().removeAttribute(SaTokenConsts.TOKEN_ACTIVITY_TIMEOUT_CHECKED_KEY); } + /** * 检查指定token 是否已经[临时过期],如果已经过期则抛出异常 * @param tokenValue 指定token @@ -572,6 +621,7 @@ public class StpLogic { request.setAttribute(SaTokenConsts.TOKEN_ACTIVITY_TIMEOUT_CHECKED_KEY, true); } + /** * 检查当前token 是否已经[临时过期],如果已经过期则抛出异常 */ @@ -579,6 +629,7 @@ public class StpLogic { checkActivityTimeout(getTokenValue()); } + /** * 续签指定token:(将 [最后操作时间] 更新为当前时间戳) * @param tokenValue 指定token @@ -591,6 +642,7 @@ public class StpLogic { SaTokenManager.getSaTokenDao().updateValue(getKeyLastActivityTime(tokenValue), String.valueOf(System.currentTimeMillis())); } + /** * 续签当前token:(将 [最后操作时间] 更新为当前时间戳) *

请注意: 即时token已经 [临时过期] 也可续签成功, @@ -827,7 +879,6 @@ public class StpLogic { // =================== id 反查token 相关操作 =================== - /** * 获取指定loginId的tokenValue *

在配置为允许并发登录时,此方法只会返回队列的最后一个token, @@ -836,16 +887,38 @@ public class StpLogic { * @return token值 */ public String getTokenValueByLoginId(Object loginId) { - List tokenValueList = getTokenValueListByLoginId(loginId); + return getTokenValueByLoginId(loginId, SaTokenConsts.DEFAULT_LOGIN_DEVICE); + } + + /** + * 获取指定loginId指定设备端的tokenValue + *

在配置为允许并发登录时,此方法只会返回队列的最后一个token, + * 如果你需要返回此账号id的所有token,请调用 getTokenValueListByLoginId + * @param loginId 账号id + * @param device 设备标识 + * @return token值 + */ + public String getTokenValueByLoginId(Object loginId, String device) { + List tokenValueList = getTokenValueListByLoginId(loginId, device); return tokenValueList.size() == 0 ? null : tokenValueList.get(tokenValueList.size() - 1); } /** - * 获取指定loginId的tokenValue + * 获取指定loginId的tokenValue集合 * @param loginId 账号id * @return 此loginId的所有相关token */ public List getTokenValueListByLoginId(Object loginId) { + return getTokenValueListByLoginId(loginId, SaTokenConsts.DEFAULT_LOGIN_DEVICE); + } + + /** + * 获取指定loginId指定设备端的tokenValue 集合 + * @param loginId 账号id + * @param device 设备标识 + * @return 此loginId的所有相关token + */ + public List getTokenValueListByLoginId(Object loginId, String device) { // 如果session为null的话直接返回空集合 SaSession session = getSessionByLoginId(loginId, false); if(session == null) { @@ -855,11 +928,42 @@ public class StpLogic { List tokenSignList = session.getTokenSignList(); List tokenValueList = new ArrayList<>(); for (TokenSign tokenSign : tokenSignList) { - tokenValueList.add(tokenSign.getValue()); + if(tokenSign.getDevice().equals(device)) { + tokenValueList.add(tokenSign.getValue()); + } } return tokenValueList; } + /** + * 返回当前token的登录设备 + * @return 当前令牌的登录设备 + */ + public String getLoginDevice() { + // 如果没有token,直接返回 + String tokenValue = getTokenValue(); + if(tokenValue == null) { + return null; + } + // 如果还未登录,直接返回 null + if(isLogin() == false) { + return null; + } + // 如果session为null的话直接返回 null + SaSession session = getSession(false); + if(session == null) { + return null; + } + // 遍历解析 + List tokenSignList = session.getTokenSignList(); + for (TokenSign tokenSign : tokenSignList) { + if(tokenSign.getValue().equals(tokenValue)) { + return tokenSign.getDevice(); + } + } + return null; + } + // =================== 返回相应key =================== @@ -870,6 +974,7 @@ public class StpLogic { public String getKeyTokenName() { return getConfig().getTokenName(); } + /** * 获取key: tokenValue 持久化 token-id * @param tokenValue token值 @@ -878,6 +983,7 @@ public class StpLogic { public String getKeyTokenValue(String tokenValue) { return getConfig().getTokenName() + ":" + loginKey + ":token:" + tokenValue; } + /** * 获取key: session 持久化 * @param loginId 账号id @@ -886,6 +992,7 @@ public class StpLogic { public String getKeySession(Object loginId) { return getConfig().getTokenName() + ":" + loginKey + ":session:" + loginId; } + /** * 获取key: tokenValue的专属session * @param tokenValue token值 @@ -894,6 +1001,7 @@ public class StpLogic { public String getKeyTokenSession(String tokenValue) { return getConfig().getTokenName() + ":" + loginKey + ":token-session:" + tokenValue; } + /** * 获取key: 指定token的最后操作时间 持久化 * @param tokenValue token值 diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpUtil.java b/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpUtil.java index 2d98ec82..786f3ad8 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpUtil.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpUtil.java @@ -61,6 +61,15 @@ public class StpUtil { stpLogic.setLoginId(loginId); } + /** + * 在当前会话上登录id + * @param loginId 登录id,建议的类型:(long | int | String) + * @param device 设备标识 + */ + public static void setLoginId(Object loginId, String device) { + stpLogic.setLoginId(loginId, device); + } + /** * 当前会话注销登录 */ @@ -85,6 +94,16 @@ public class StpUtil { stpLogic.logoutByLoginId(loginId); } + /** + * 指定loginId指定设备的会话注销登录(踢人下线) + *

当对方再次访问系统时,会抛出NotLoginException异常,场景值=-2 + * @param loginId 账号id + * @param device 设备标识 + */ + public static void logoutByLoginId(Object loginId, String device) { + stpLogic.logoutByLoginId(loginId, device); + } + // 查询相关 @@ -381,15 +400,44 @@ public class StpUtil { public static String getTokenValueByLoginId(Object loginId) { return stpLogic.getTokenValueByLoginId(loginId); } + + /** + * 获取指定loginId指定设备端的tokenValue + *

在配置为允许并发登录时,此方法只会返回队列的最后一个token, + * 如果你需要返回此账号id的所有token,请调用 getTokenValueListByLoginId + * @param loginId 账号id + * @param device 设备标识 + * @return token值 + */ + public static String getTokenValueByLoginId(Object loginId, String device) { + return stpLogic.getTokenValueByLoginId(loginId, device); + } /** - * 获取指定loginId的tokenValue + * 获取指定loginId的tokenValue集合 * @param loginId 账号id * @return 此loginId的所有相关token */ public static List getTokenValueListByLoginId(Object loginId) { return stpLogic.getTokenValueListByLoginId(loginId); } + + /** + * 获取指定loginId指定设备端的tokenValue集合 + * @param loginId 账号id + * @param device 设备标识 + * @return 此loginId的所有相关token + */ + public static List getTokenValueListByLoginId(Object loginId, String device) { + return stpLogic.getTokenValueListByLoginId(loginId, device); + } + /** + * 返回当前token的登录设备 + * @return 当前令牌的登录设备 + */ + public static String getLoginDevice() { + return stpLogic.getLoginDevice(); + } } diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/util/SaTokenConsts.java b/sa-token-core/src/main/java/cn/dev33/satoken/util/SaTokenConsts.java index 10e8decd..a5704f65 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/util/SaTokenConsts.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/util/SaTokenConsts.java @@ -10,7 +10,7 @@ public class SaTokenConsts { /** * sa-token 版本号 */ - public static final String VERSION_NO = "v1.8.0"; + public static final String VERSION_NO = "v1.9.0"; /** * sa-token 开源地址 diff --git a/sa-token-dao-redis-jackson/pom.xml b/sa-token-dao-redis-jackson/pom.xml index 293c235a..5adde229 100644 --- a/sa-token-dao-redis-jackson/pom.xml +++ b/sa-token-dao-redis-jackson/pom.xml @@ -7,7 +7,7 @@ cn.dev33 sa-token-parent - 1.8.0 + 1.9.0 jar @@ -20,13 +20,13 @@ cn.dev33 sa-token-spring-boot-starter - 1.8.0 + 1.9.0 org.springframework.boot spring-boot-starter-data-redis - 2.3.7.RELEASE + 2.3.3.RELEASE diff --git a/sa-token-dao-redis/pom.xml b/sa-token-dao-redis/pom.xml index 9e66756a..9d5f072b 100644 --- a/sa-token-dao-redis/pom.xml +++ b/sa-token-dao-redis/pom.xml @@ -7,7 +7,7 @@ cn.dev33 sa-token-parent - 1.8.0 + 1.9.0 jar @@ -20,13 +20,13 @@ cn.dev33 sa-token-spring-boot-starter - 1.8.0 + 1.9.0 org.springframework.boot spring-boot-starter-data-redis - 2.3.7.RELEASE + 2.3.3.RELEASE diff --git a/sa-token-demo-springboot/pom.xml b/sa-token-demo-springboot/pom.xml index bc599554..09bb81d1 100644 --- a/sa-token-demo-springboot/pom.xml +++ b/sa-token-demo-springboot/pom.xml @@ -29,21 +29,21 @@ cn.dev33 sa-token-spring-boot-starter - 1.8.0 + 1.9.0 diff --git a/sa-token-demo-springboot/src/main/java/com/pj/satoken/MySaTokenAction.java b/sa-token-demo-springboot/src/main/java/com/pj/satoken/MySaTokenAction.java deleted file mode 100644 index 7327f1d4..00000000 --- a/sa-token-demo-springboot/src/main/java/com/pj/satoken/MySaTokenAction.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.pj.satoken; - -import org.springframework.stereotype.Component; - -import cn.dev33.satoken.action.SaTokenActionDefaultImpl; - -/** - * 继承sa-token行为Bean默认实现, 重写部分逻辑 - * @author kong - * - */ -@Component -public class MySaTokenAction extends SaTokenActionDefaultImpl { - - // 重写token生成策略 -// @Override -// public String createToken(Object loginId, String loginKey) { -// return SaTokenInsideUtil.getRandomString(60); -// } - -} diff --git a/sa-token-demo-springboot/src/main/java/com/pj/test/TestController.java b/sa-token-demo-springboot/src/main/java/com/pj/test/TestController.java index 096ac1c4..a45f1f7a 100644 --- a/sa-token-demo-springboot/src/main/java/com/pj/test/TestController.java +++ b/sa-token-demo-springboot/src/main/java/com/pj/test/TestController.java @@ -11,6 +11,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import cn.dev33.satoken.annotation.SaCheckLogin; import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.dev33.satoken.annotation.SaCheckRole; import cn.dev33.satoken.annotation.SaMode; import cn.dev33.satoken.session.SaSessionCustomUtil; import cn.dev33.satoken.stp.SaTokenInfo; @@ -38,7 +39,8 @@ public class TestController { System.out.println("登录成功"); System.out.println("当前是否登录:" + StpUtil.isLogin()); System.out.println("当前登录账号:" + StpUtil.getLoginId()); - System.out.println("当前登录账号:" + StpUtil.getLoginIdAsInt()); // 获取登录id并转为int +// System.out.println("当前登录账号并转为int:" + StpUtil.getLoginIdAsInt()); + System.out.println("当前登录设备:" + StpUtil.getLoginDevice()); // System.out.println("当前token信息:" + StpUtil.getTokenInfo()); return AjaxJson.getSuccess(); @@ -151,13 +153,14 @@ public class TestController { // 测试注解式鉴权, 浏览器访问: http://localhost:8081/test/atCheck @SaCheckLogin // 注解式鉴权:当前会话必须登录才能通过 + @SaCheckRole("super-admin") // 注解式鉴权:当前会话必须具有指定角色标识才能通过 @SaCheckPermission("user-add") // 注解式鉴权:当前会话必须具有指定权限才能通过 @RequestMapping("atCheck") public AjaxJson atCheck() { System.out.println("======================= 进入方法,测试注解鉴权接口 ========================= "); System.out.println("只有通过注解鉴权,才能进入此方法"); - StpUtil.checkActivityTimeout(); - StpUtil.updateLastActivityToNow(); +// StpUtil.checkActivityTimeout(); +// StpUtil.updateLastActivityToNow(); return AjaxJson.getSuccess(); } @@ -192,10 +195,18 @@ public class TestController { // 测试 浏览器访问: http://localhost:8081/test/test @RequestMapping("test") public AjaxJson test() { - StpUtil.getTokenSession().logout(); +// StpUtil.getTokenSession().logout(); + StpUtil.logoutByLoginId(10001); return AjaxJson.getSuccess(); } + + // 测试登录接口, 按照设备登录, 浏览器访问: http://localhost:8081/test/login2 + @RequestMapping("login2") + public AjaxJson login2(@RequestParam(defaultValue="10001") String id, @RequestParam(defaultValue="PC") String device) { + StpUtil.setLoginId(id, device); + return AjaxJson.getSuccess(); + } } diff --git a/sa-token-doc/doc/README.md b/sa-token-doc/doc/README.md index c30805e6..3e5e92db 100644 --- a/sa-token-doc/doc/README.md +++ b/sa-token-doc/doc/README.md @@ -1,11 +1,11 @@

logo

-

sa-token v1.8.0

+

sa-token v1.9.0

一个JavaWeb轻量级权限认证框架,功能全面,上手简单

- + @@ -16,7 +16,7 @@ --- -## 😘 在线资料 +## 在线资料 - [官网首页:http://sa-token.dev33.cn/](http://sa-token.dev33.cn/) @@ -27,8 +27,8 @@ - [开源不易,求鼓励,点个star吧](https://github.com/click33/sa-token) -## ⭐ sa-token是什么? -**sa-token是一个JavaWeb轻量级权限认证框架,其API调用非常简单,有多简单呢?以登录验证为例,你只需要:** +## sa-token是什么? +sa-token是一个JavaWeb轻量级权限认证框架,其API调用非常简单,有多简单呢?以登录验证为例,你只需要: ``` java // 在登录时写入当前会话的账号id @@ -39,16 +39,16 @@ StpUtil.checkLogin(); ``` -**没有复杂的封装!不要任何的配置!只需这两行简单的调用,即可轻松完成系统登录鉴权!** +没有复杂的封装!不要任何的配置!只需这两行简单的调用,即可轻松完成系统登录鉴权! -## 🔥 框架设计思想 +## 框架设计思想 与其它权限认证框架相比,`sa-token`尽力保证两点: - 上手简单:能自动化的配置全部自动化,不让你费脑子 - 功能强大:能涵盖的功能全部涵盖,不让你用个框架还要自己给框架打各种补丁 -**如果上面的示例能够证明`sa-token`的简单,那么以下API则可以证明`sa-token`的强大** +如果上面的示例能够证明`sa-token`的简单,那么以下API则可以证明`sa-token`的强大 ``` java StpUtil.setLoginId(10001); // 标记当前会话登录的账号id StpUtil.getLoginId(); // 获取当前会话登录的账号id @@ -60,13 +60,15 @@ StpUtil.hasPermission("user:add"); // 查询当前账号是否含有指定权 StpUtil.getSession(); // 获取当前账号id的Session StpUtil.getSessionByLoginId(10001); // 获取账号id为10001的Session StpUtil.getTokenValueByLoginId(10001); // 获取账号id为10001的token令牌值 +StpUtil.setLoginId(10001, "PC"); // 指定设备标识登录 +StpUtil.logoutByLoginId(10001, "PC"); // 指定设备标识进行强制注销 (不同端不受影响) ``` -**sa-token的API众多,请恕此处无法为您逐一展示,更多示例请戳官方在线文档** +sa-token的API众多,请恕此处无法为您逐一展示,更多示例请戳官方在线文档 -## 💦️️ 涵盖功能 +## 涵盖功能 - **登录验证** —— 轻松登录鉴权,并提供五种细分场景值 - **权限验证** —— 拦截违规调用,不同角色不同授权 - **Session会话** —— 专业的数据缓存中心 @@ -78,11 +80,12 @@ StpUtil.getTokenValueByLoginId(10001); // 获取账号id为10001的token令牌 - **注解式鉴权** —— 优雅的将鉴权与业务代码分离 - **花式token生成** —— 内置六种token风格,还可自定义token生成策略 - **自动续签** —— 提供两种token过期策略,灵活搭配使用,还可自动续签 +- **同端互斥登录** —— 像QQ一样手机电脑同时在线,但是两个手机上互斥登录 - **组件自动注入** —— 零配置与Spring等框架集成 - **更多功能正在集成中...** —— 如有您有好想法或者建议,欢迎加群交流 -## 🔨 贡献代码 +## 贡献代码 sa-token欢迎大家贡献代码,为框架添砖加瓦 1. 在github上fork一份到自己的仓库 2. clone自己的仓库到本地电脑 @@ -91,7 +94,7 @@ sa-token欢迎大家贡献代码,为框架添砖加瓦 5. 等待合并 -## 🌱 建议贡献的地方 +## 建议贡献的地方 - 修复源码现有bug,或增加新的实用功能 - 完善在线文档,或者修复现有错误之处 - 更多demo示例:比如SSM版搭建步骤 @@ -99,11 +102,11 @@ sa-token欢迎大家贡献代码,为框架添砖加瓦 - 如果更新实用功能,可在文档友情链接处留下自己的推广链接 -## 🚀 友情链接 +## 友情链接 [**[ okhttps ]** 一个轻量级http通信框架,支持 WebSocket 以及 Stomp 协议](https://gitee.com/ejlchina-zhxu/okhttps) -## 😎 交流群 +## 交流群 QQ交流群:[1002350610 点击加入](https://jq.qq.com/?_wv=1027&k=45H977HM) ,欢迎你的加入 diff --git a/sa-token-doc/doc/_sidebar.md b/sa-token-doc/doc/_sidebar.md index f0db84c1..e76e2dce 100644 --- a/sa-token-doc/doc/_sidebar.md +++ b/sa-token-doc/doc/_sidebar.md @@ -14,6 +14,7 @@ - [无Cookie模式(前后台分离)](/use/not-cookie) - [模拟他人](/use/mock-person) - [多账号验证](/use/many-account) + - [同端互斥登录](/use/mutex-login) - [注解式鉴权](/use/at-check) - [花式token](/use/token-style) - [框架配置](/use/config) diff --git a/sa-token-doc/doc/index.html b/sa-token-doc/doc/index.html index 511f2746..4dbf9360 100644 --- a/sa-token-doc/doc/index.html +++ b/sa-token-doc/doc/index.html @@ -2,13 +2,13 @@ - sa-token + sa-token 官方文档 - - + + @@ -22,6 +22,7 @@