From f59b1b3885b075953eb5848b7c8b3989563df27b Mon Sep 17 00:00:00 2001 From: click33 <2393584716@qq.com> Date: Wed, 7 Jun 2023 15:49:04 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E9=83=A8=E5=88=86=20TokenSig?= =?UTF-8?q?n=20=E7=9B=B8=E5=85=B3=E6=96=B9=E6=B3=95=EF=BC=8C=E6=9B=B4?= =?UTF-8?q?=E6=96=B9=E4=BE=BF=E7=9A=84=E6=93=8D=E4=BD=9C=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E4=BC=9A=E8=AF=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/dev33/satoken/session/SaSession.java | 68 ++++++++++++++----- .../java/cn/dev33/satoken/stp/StpLogic.java | 58 ++++++++++------ .../java/cn/dev33/satoken/stp/StpUtil.java | 22 ++++++ .../cn/dev33/satoken/strategy/SaStrategy.java | 59 ++++++++-------- .../main/java/com/pj/test/TestController.java | 3 +- 5 files changed, 142 insertions(+), 68 deletions(-) diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/session/SaSession.java b/sa-token-core/src/main/java/cn/dev33/satoken/session/SaSession.java index 8437cbb2..83d8bc2e 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/session/SaSession.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/session/SaSession.java @@ -15,20 +15,16 @@ */ package cn.dev33.satoken.session; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.Vector; -import java.util.concurrent.ConcurrentHashMap; - import cn.dev33.satoken.SaManager; import cn.dev33.satoken.application.SaSetValueInterface; import cn.dev33.satoken.dao.SaTokenDao; import cn.dev33.satoken.listener.SaTokenEventCenter; import cn.dev33.satoken.util.SaFoxUtil; +import java.io.Serializable; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + /** * Session Model,会话作用域的读取值对象 * @@ -277,14 +273,15 @@ public class SaSession implements SaSetValueInterface, Serializable { * @param device 设备类型,填 null 代表不限设备类型 * @return token签名列表 */ - public List tokenSignListCopyByDevice(String device) { + public List getTokenSignListByDevice(String device) { // 返回全部 if(device == null) { return tokenSignListCopy(); } - // 返回筛选后的 + // 返回筛选后的 + List tokenSignList = tokenSignListCopy(); List list = new ArrayList<>(); - for (TokenSign tokenSign : tokenSignListCopy()) { + for (TokenSign tokenSign : tokenSignList) { if(SaFoxUtil.equals(tokenSign.getDevice(), device)) { list.add(tokenSign); } @@ -292,6 +289,24 @@ public class SaSession implements SaSetValueInterface, Serializable { return list; } + /** + * 获取当前 Session 上的所有 token 列表 + * + * @param device 设备类型,填 null 代表不限设备类型 + * @return 此 loginId 的所有登录 token + */ + public List getTokenValueListByDevice(String device) { + // 遍历解析,按照设备类型进行筛选 + List tokenSignList = tokenSignListCopy(); + List tokenValueList = new ArrayList<>(); + for (TokenSign tokenSign : tokenSignList) { + if(device == null || tokenSign.getDevice().equals(device)) { + tokenValueList.add(tokenSign.getValue()); + } + } + return tokenValueList; + } + /** * 查找一个 Token 签名 * @@ -313,13 +328,18 @@ public class SaSession implements SaSetValueInterface, Serializable { * @param tokenSign Token 签名 */ public void addTokenSign(TokenSign tokenSign) { - // 如果已经存在于列表中,则无需再次添加 - if(getTokenSign(tokenSign.getValue()) != null) { - return; + // 根据 tokenValue 值查重,如果不存在,则添加 + TokenSign oldTokenSign = getTokenSign(tokenSign.getValue()); + if(oldTokenSign == null) { + tokenSignList.add(tokenSign); + update(); + } else { + // 如果存在,则更新 + oldTokenSign.setValue(tokenSign.getValue()); + oldTokenSign.setDevice(tokenSign.getDevice()); + oldTokenSign.setTag(tokenSign.getTag()); + update(); } - // 添加并更新 - tokenSignList.add(tokenSign); - update(); } /** @@ -512,4 +532,18 @@ public class SaSession implements SaSetValueInterface, Serializable { this.update(); } + // + + + /** + * 请更换为:getTokenSignListByDevice(device) + * + * @param device 设备类型,填 null 代表不限设备类型 + * @return token签名列表 + */ + @Deprecated + public List tokenSignListCopyByDevice(String device) { + return getTokenSignListByDevice(device); + } + } 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 49f0b677..fe8a618c 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 @@ -36,7 +36,6 @@ import cn.dev33.satoken.util.SaFoxUtil; import cn.dev33.satoken.util.SaTokenConsts; import cn.dev33.satoken.util.SaValue2Box; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; @@ -490,8 +489,8 @@ public class StpLogic { // 3.1、看看全局配置的 IsShare 参数,配置为 true 才是允许复用旧 token if(getConfigOfIsShare()) { - // 根据账号id + 设备标识,尝试获取旧的 token - String tokenValue = getTokenValueByLoginId(id, loginModel.getDeviceOrDefault()); + // 根据账号id,尝试获取旧的 token + String tokenValue = getTokenValueByLoginId(id, null); // 如果有值,那就直接复用 if(SaFoxUtil.isNotEmpty(tokenValue)) { @@ -605,7 +604,7 @@ public class StpLogic { if(session != null) { // 2、遍历此账号所有从这个 device 设备上登录的客户端,清除相关数据 - for (TokenSign tokenSign: session.tokenSignListCopyByDevice(device)) { + for (TokenSign tokenSign: session.getTokenSignListByDevice(device)) { // 2.1、获取此客户端的 token 值 String tokenValue = tokenSign.getValue(); @@ -652,7 +651,7 @@ public class StpLogic { } // 2、获取这个账号指定设备类型下的所有登录客户端 - List list = session.tokenSignListCopyByDevice(device); + List list = session.getTokenSignListByDevice(device); // 3、按照登录时间倒叙,超过 maxLoginCount 数量的,全部注销掉 for (int i = 0; i < list.size() - maxLoginCount; i++) { @@ -741,7 +740,7 @@ public class StpLogic { if(session != null) { // 2、遍历此账号所有从这个 device 设备上登录的客户端,清除相关数据 - for (TokenSign tokenSign: session.tokenSignListCopyByDevice(device)) { + for (TokenSign tokenSign: session.getTokenSignListByDevice(device)) { // 2.1、获取此客户端的 token 值 String tokenValue = tokenSign.getValue(); @@ -817,7 +816,7 @@ public class StpLogic { if(session != null) { // 2、遍历此账号所有从这个 device 设备上登录的客户端,清除相关数据 - for (TokenSign tokenSign: session.tokenSignListCopyByDevice(device)) { + for (TokenSign tokenSign: session.getTokenSignListByDevice(device)) { // 2.1、获取此客户端的 token 值 String tokenValue = tokenSign.getValue(); @@ -858,8 +857,18 @@ public class StpLogic { // 2、并且不在异常项集合里(此项在 getLoginIdDefaultNull() 方法里完成判断) return getLoginIdDefaultNull() != null; } - - /** + + /** + * 判断指定账号是否已经登录 + * + * @return 已登录返回 true,未登录返回 false + */ + public boolean isLogin(Object loginId) { + // 判断条件:能否根据 loginId 查询到对应的 tokenSign 值 + return getTokenSignListByLoginId(loginId, null).size() > 0; + } + + /** * 检验当前会话是否已经登录,如未登录,则抛出异常 */ public void checkLogin() { @@ -1989,17 +1998,28 @@ public class StpLogic { return Collections.emptyList(); } - // 遍历解析,按照设备类型进行筛选 - List tokenSignList = session.tokenSignListCopy(); - List tokenValueList = new ArrayList<>(); - for (TokenSign tokenSign : tokenSignList) { - if(device == null || tokenSign.getDevice().equals(device)) { - tokenValueList.add(tokenSign.getValue()); - } - } - return tokenValueList; + // 按照设备类型进行筛选 + return session.getTokenValueListByDevice(device); } - + + /** + * 获取指定账号 id 指定设备类型端的 tokenSign 集合 + * + * @param loginId 账号id + * @param device 设备类型,填 null 代表不限设备类型 + * @return 此 loginId 的所有登录 token + */ + public List getTokenSignListByLoginId(Object loginId, String device) { + // 如果该账号的 Account-Session 为 null,说明此账号尚没有客户端在登录,此时返回空集合 + SaSession session = getSessionByLoginId(loginId, false); + if(session == null) { + return Collections.emptyList(); + } + + // 按照设备类型进行筛选 + return session.getTokenSignListByDevice(device); + } + /** * 返回当前会话的登录设备类型 * 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 945c88d9..05275467 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 @@ -19,7 +19,9 @@ import cn.dev33.satoken.SaManager; import cn.dev33.satoken.fun.SaFunction; import cn.dev33.satoken.listener.SaTokenEventCenter; import cn.dev33.satoken.session.SaSession; +import cn.dev33.satoken.session.TokenSign; +import java.util.Collections; import java.util.List; /** @@ -314,6 +316,15 @@ public class StpUtil { return stpLogic.isLogin(); } + /** + * 判断指定账号是否已经登录 + * + * @return 已登录返回 true,未登录返回 false + */ + public static boolean isLogin(Object loginId) { + return stpLogic.isLogin(loginId); + } + /** * 检验当前会话是否已经登录,如未登录,则抛出异常 */ @@ -814,6 +825,17 @@ public class StpUtil { return stpLogic.getTokenValueListByLoginId(loginId, device); } + /** + * 获取指定账号 id 指定设备类型端的 tokenSign 集合 + * + * @param loginId 账号id + * @param device 设备类型,填 null 代表不限设备类型 + * @return 此 loginId 的所有登录 tokenSign + */ + public static List getTokenSignListByLoginId(Object loginId, String device) { + return stpLogic.getTokenSignListByLoginId(loginId, device); + } + /** * 返回当前会话的登录设备类型 * diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/strategy/SaStrategy.java b/sa-token-core/src/main/java/cn/dev33/satoken/strategy/SaStrategy.java index ee1afc75..6e1b9262 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/strategy/SaStrategy.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/strategy/SaStrategy.java @@ -29,21 +29,20 @@ import cn.dev33.satoken.util.SaTokenConsts; import java.util.ArrayList; import java.util.List; import java.util.UUID; -import java.util.function.Function; /** - * Sa-Token 策略对象 + * Sa-Token 策略对象 *

* 此类统一定义框架内的一些关键性逻辑算法,方便开发者进行按需重写,例: *

*
-	// SaStrategy全局单例,所有方法都用以下形式重写 
-	SaStrategy.instance.setCreateToken((loginId, loginType) -》 {
-		// 自定义Token生成的算法 
-		return "xxxx";
-	});
+ // SaStrategy全局单例,所有方法都用以下形式重写
+ SaStrategy.instance.setCreateToken((loginId, loginType) -》 {
+ // 自定义Token生成的算法
+ return "xxxx";
+ });
  * 
- * + * * @author click33 * @since 1.27.0 */ @@ -53,18 +52,18 @@ public final class SaStrategy { } /** - * 获取 SaStrategy 对象的单例引用 + * 获取 SaStrategy 对象的单例引用 */ public static final SaStrategy instance = new SaStrategy(); // ----------------------- 所有策略 - + /** * 创建 Token 的策略 */ public SaCreateTokenFunction createToken = (loginId, loginType) -> { - // 根据配置的tokenStyle生成不同风格的token + // 根据配置的tokenStyle生成不同风格的token String tokenStyle = SaManager.getConfig().getTokenStyle(); switch (tokenStyle) { @@ -99,7 +98,7 @@ public final class SaStrategy { return UUID.randomUUID().toString(); } }; - + /** * 创建 Session 的策略 */ @@ -117,19 +116,19 @@ public final class SaStrategy { return false; } - // 先尝试一下简单匹配,如果可以匹配成功则无需继续模糊匹配 + // 先尝试一下简单匹配,如果可以匹配成功则无需继续模糊匹配 if (list.contains(element)) { return true; } - - // 开始模糊匹配 + + // 开始模糊匹配 for (String patt : list) { if(SaFoxUtil.vagueMatch(patt, element)) { return true; } } - - // 走出for循环说明没有一个元素可以匹配成功 + + // 走出for循环说明没有一个元素可以匹配成功 return false; }; @@ -138,10 +137,10 @@ public final class SaStrategy { */ public SaCheckMethodAnnotationFunction checkMethodAnnotation = (method) -> { - // 先校验 Method 所属 Class 上的注解 + // 先校验 Method 所属 Class 上的注解 instance.checkElementAnnotation.accept(method.getDeclaringClass()); - // 再校验 Method 上的注解 + // 再校验 Method 上的注解 instance.checkElementAnnotation.accept(method); }; @@ -150,18 +149,18 @@ public final class SaStrategy { */ public SaCheckElementAnnotationFunction checkElementAnnotation = (element) -> { - // 校验 @SaCheckLogin 注解 + // 校验 @SaCheckLogin 注解 SaCheckLogin checkLogin = (SaCheckLogin) SaStrategy.instance.getAnnotation.apply(element, SaCheckLogin.class); if(checkLogin != null) { SaManager.getStpLogic(checkLogin.type(), false).checkByAnnotation(checkLogin); } - - // 校验 @SaCheckRole 注解 + + // 校验 @SaCheckRole 注解 SaCheckRole checkRole = (SaCheckRole) SaStrategy.instance.getAnnotation.apply(element, SaCheckRole.class); if(checkRole != null) { SaManager.getStpLogic(checkRole.type(), false).checkByAnnotation(checkRole); } - + // 校验 @SaCheckPermission 注解 SaCheckPermission checkPermission = (SaCheckPermission) SaStrategy.instance.getAnnotation.apply(element, SaCheckPermission.class); if(checkPermission != null) { @@ -179,7 +178,7 @@ public final class SaStrategy { if(checkDisable != null) { SaManager.getStpLogic(checkDisable.type(), false).checkByAnnotation(checkDisable); } - + // 校验 @SaCheckBasic 注解 SaCheckBasic checkBasic = (SaCheckBasic) SaStrategy.instance.getAnnotation.apply(element, SaCheckBasic.class); if(checkBasic != null) { @@ -283,7 +282,7 @@ public final class SaStrategy { * 从元素上获取注解 */ public SaGetAnnotationFunction getAnnotation = (element, annotationClass)->{ - // 默认使用jdk的注解处理器 + // 默认使用jdk的注解处理器 return element.getAnnotation(annotationClass); }; @@ -337,7 +336,7 @@ public final class SaStrategy { /** * 重写创建 Token 的策略 * - * @param createToken / + * @param createToken / * @return / */ public SaStrategy setCreateToken(SaCreateTokenFunction createToken) { @@ -348,7 +347,7 @@ public final class SaStrategy { /** * 重写创建 Session 的策略 * - * @param createSession / + * @param createSession / * @return / */ public SaStrategy setCreateSession(SaCreateSessionFunction createSession) { @@ -381,7 +380,7 @@ public final class SaStrategy { /** * 对一个 [元素] 对象进行注解校验 (注解鉴权内部实现) * - * @param checkElementAnnotation / + * @param checkElementAnnotation / * @return / */ public SaStrategy setCheckElementAnnotation(SaCheckElementAnnotationFunction checkElementAnnotation) { @@ -404,7 +403,7 @@ public final class SaStrategy { /** * 从元素上获取注解 * - * @param getAnnotation / + * @param getAnnotation / * @return / */ public SaStrategy setGetAnnotation(SaGetAnnotationFunction getAnnotation) { @@ -415,7 +414,7 @@ public final class SaStrategy { /** * 判断一个 Method 或其所属 Class 是否包含指定注解 * - * @param isAnnotationPresent / + * @param isAnnotationPresent / * @return / */ public SaStrategy setIsAnnotationPresent(SaIsAnnotationPresentFunction isAnnotationPresent) { diff --git a/sa-token-demo/sa-token-demo-test/src/main/java/com/pj/test/TestController.java b/sa-token-demo/sa-token-demo-test/src/main/java/com/pj/test/TestController.java index cf2c5ba4..9e7ff6a6 100644 --- a/sa-token-demo/sa-token-demo-test/src/main/java/com/pj/test/TestController.java +++ b/sa-token-demo/sa-token-demo-test/src/main/java/com/pj/test/TestController.java @@ -1,6 +1,5 @@ package com.pj.test; -import cn.dev33.satoken.stp.SaLoginConfig; import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.util.SaFoxUtil; import cn.dev33.satoken.util.SaResult; @@ -22,7 +21,7 @@ public class TestController { // 测试登录 ---- http://localhost:8081/test/login @RequestMapping("login") public SaResult login(@RequestParam(defaultValue = "10001") long id) { - StpUtil.login(id, SaLoginConfig.setActiveTimeout(20)); + StpUtil.login(id); return SaResult.ok("登录成功"); }