diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/stp/SaLoginModel.java b/sa-token-core/src/main/java/cn/dev33/satoken/stp/SaLoginModel.java index c2aa1609..f5e0bc3b 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/stp/SaLoginModel.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/stp/SaLoginModel.java @@ -90,6 +90,16 @@ public class SaLoginModel { return (int)(long)timeout; } + /** + * @return 获取device参数,如果为null,则返回默认值 + */ + public String getDeviceOrDefalut() { + if(device == null) { + return SaTokenConsts.DEFAULT_LOGIN_DEVICE; + } + return device; + } + /** * 构建对象,初始化默认值 * @return 对象自身 @@ -104,9 +114,9 @@ public class SaLoginModel { * @return 对象自身 */ public SaLoginModel build(SaTokenConfig config) { - if(device == null) { - device = SaTokenConsts.DEFAULT_LOGIN_DEVICE; - } +// if(device == null) { +// device = SaTokenConsts.DEFAULT_LOGIN_DEVICE; +// } if(isLastingCookie == null) { isLastingCookie = true; } 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 f09c86b7..c358a6c4 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 @@ -253,7 +253,7 @@ public class StpLogic { session.updateMinTimeout(loginModel.getTimeout()); // 在 User-Session 上记录token签名 - session.addTokenSign(tokenValue, loginModel.getDevice()); + session.addTokenSign(tokenValue, loginModel.getDeviceOrDefalut()); // ------ 4. 持久化其它数据 // token -> id 映射关系 @@ -974,7 +974,7 @@ public class StpLogic { * @return 是否含有指定角色标识 */ public boolean hasRole(String role) { - return hasRole(getLoginId(), role); + return isLogin() && hasRole(getLoginId(), role); } /** @@ -986,7 +986,7 @@ public class StpLogic { try { checkRoleAnd(roleArray); return true; - } catch (NotRoleException e) { + } catch (NotLoginException | NotRoleException e) { return false; } } @@ -1000,7 +1000,7 @@ public class StpLogic { try { checkRoleOr(roleArray); return true; - } catch (NotRoleException e) { + } catch (NotLoginException | NotRoleException e) { return false; } } @@ -1046,6 +1046,16 @@ public class StpLogic { throw new NotRoleException(roleArray[0], this.loginType); } } + + // -- + /** + * 返回当前账号所拥有的角色标识集合 + * @return / + */ + public List getRoleList() { + return SaManager.getStpInterface().getRoleList(getLoginId(), loginType); + } + // ------------------- 权限验证操作 ------------------- @@ -1067,7 +1077,7 @@ public class StpLogic { * @return 是否含有指定权限 */ public boolean hasPermission(String permission) { - return hasPermission(getLoginId(), permission); + return isLogin() && hasPermission(getLoginId(), permission); } /** @@ -1079,7 +1089,7 @@ public class StpLogic { try { checkPermissionAnd(permissionArray); return true; - } catch (NotPermissionException e) { + } catch (NotLoginException | NotPermissionException e) { return false; } } @@ -1093,7 +1103,7 @@ public class StpLogic { try { checkPermissionOr(permissionArray); return true; - } catch (NotPermissionException e) { + } catch (NotLoginException | NotPermissionException e) { return false; } } @@ -1140,10 +1150,17 @@ public class StpLogic { } } - + // -- + /** + * 返回当前账号所拥有的权限码集合 + * @return / + */ + public List getPermissionList() { + return SaManager.getStpInterface().getPermissionList(getLoginId(), loginType); + } - // ------------------- id 反查token 相关操作 ------------------- + // ------------------- id 反查 token 相关操作 ------------------- /** * 获取指定账号id的tokenValue @@ -1153,7 +1170,7 @@ public class StpLogic { * @return token值 */ public String getTokenValueByLoginId(Object loginId) { - return getTokenValueByLoginId(loginId, SaTokenConsts.DEFAULT_LOGIN_DEVICE); + return getTokenValueByLoginId(loginId, null); } /** @@ -1161,7 +1178,7 @@ public class StpLogic { *

在配置为允许并发登录时,此方法只会返回队列的最后一个token, * 如果你需要返回此账号id的所有token,请调用 getTokenValueListByLoginId * @param loginId 账号id - * @param device 设备标识 + * @param device 设备标识,填null代表不限设备 * @return token值 */ public String getTokenValueByLoginId(Object loginId, String device) { @@ -1175,13 +1192,13 @@ public class StpLogic { * @return 此loginId的所有相关token */ public List getTokenValueListByLoginId(Object loginId) { - return getTokenValueListByLoginId(loginId, SaTokenConsts.DEFAULT_LOGIN_DEVICE); + return getTokenValueListByLoginId(loginId, null); } /** * 获取指定账号id指定设备端的tokenValue 集合 * @param loginId 账号id - * @param device 设备标识 + * @param device 设备标识,填null代表不限设备 * @return 此loginId的所有相关token */ public List getTokenValueListByLoginId(Object loginId, String device) { @@ -1194,7 +1211,7 @@ public class StpLogic { List tokenSignList = session.getTokenSignList(); List tokenValueList = new ArrayList<>(); for (TokenSign tokenSign : tokenSignList) { - if(tokenSign.getDevice().equals(device)) { + if(device == null || tokenSign.getDevice().equals(device)) { tokenValueList.add(tokenSign.getValue()); } } @@ -1267,7 +1284,7 @@ public class StpLogic { } - // ------------------- 其它方法 ------------------- + // ------------------- 注解鉴权 ------------------- /** * 根据注解(@SaCheckLogin)鉴权 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 6f4272fa..d2c69e51 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 @@ -2,6 +2,7 @@ package cn.dev33.satoken.stp; import java.util.List; +import cn.dev33.satoken.SaManager; import cn.dev33.satoken.fun.SaFunction; import cn.dev33.satoken.session.SaSession; @@ -29,6 +30,16 @@ public class StpUtil { return stpLogic.getLoginType(); } + /** + * 重置 StpLogic 对象 + * @param stpLogic / + */ + public static void setStpLogic(StpLogic stpLogic) { + StpUtil.stpLogic = stpLogic; + // 防止自定义 stpLogic 被覆盖 + SaManager.putStpLogic(stpLogic); + } + // =================== 获取token 相关 =================== @@ -446,7 +457,16 @@ public class StpUtil { public static void checkRoleOr(String... roleArray){ stpLogic.checkRoleOr(roleArray); } - + + // -- + /** + * 返回当前账号所拥有的角色标识集合 + * @return / + */ + public static List getRoleList() { + return stpLogic.getRoleList(); + } + // =================== 权限验证操作 =================== @@ -511,6 +531,15 @@ public class StpUtil { stpLogic.checkPermissionOr(permissionArray); } + // -- + /** + * 返回当前账号所拥有的权限码集合 + * @return / + */ + public static List getPermissionList() { + return stpLogic.getPermissionList(); + } + // =================== id 反查token 相关操作 =================== diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/util/SaFoxUtil.java b/sa-token-core/src/main/java/cn/dev33/satoken/util/SaFoxUtil.java index 7a7a0324..3d8ec42a 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/util/SaFoxUtil.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/util/SaFoxUtil.java @@ -404,4 +404,6 @@ public class SaFoxUtil { return str; } + + } diff --git a/sa-token-demo/sa-token-demo-springboot/src/main/java/com/pj/satoken/at/StpUserUtil.java b/sa-token-demo/sa-token-demo-springboot/src/main/java/com/pj/satoken/at/StpUserUtil.java index 91c82fa8..47a70b52 100644 --- a/sa-token-demo/sa-token-demo-springboot/src/main/java/com/pj/satoken/at/StpUserUtil.java +++ b/sa-token-demo/sa-token-demo-springboot/src/main/java/com/pj/satoken/at/StpUserUtil.java @@ -2,11 +2,13 @@ package com.pj.satoken.at; import java.util.List; +import cn.dev33.satoken.SaManager; import cn.dev33.satoken.fun.SaFunction; import cn.dev33.satoken.session.SaSession; import cn.dev33.satoken.stp.SaLoginModel; import cn.dev33.satoken.stp.SaTokenInfo; import cn.dev33.satoken.stp.StpLogic; +import cn.dev33.satoken.stp.StpUtil; /** * Sa-Token 权限认证工具类 @@ -32,6 +34,16 @@ public class StpUserUtil { return stpLogic.getLoginType(); } + /** + * 重置 StpLogic 对象 + * @param stpLogic / + */ + public static void setStpLogic(StpLogic stpLogic) { + StpUtil.stpLogic = stpLogic; + // 防止自定义 stpLogic 被覆盖 + SaManager.putStpLogic(stpLogic); + } + // =================== 获取token 相关 =================== @@ -136,7 +148,7 @@ public class StpUserUtil { } /** - * 注销会话,根据指定 Token + * 会话注销,根据指定 Token * * @param tokenValue 指定token */ @@ -264,7 +276,7 @@ public class StpUserUtil { } - // =================== session相关 =================== + // =================== User-Session 相关 =================== /** * 获取指定账号id的Session, 如果Session尚未创建,isCreate=是否新建并返回 @@ -312,7 +324,7 @@ public class StpUserUtil { } - // =================== token专属session =================== + // =================== Token-Session 相关 =================== /** * 获取指定Token-Session,如果Session尚未创建,则新建并返回 @@ -354,7 +366,7 @@ public class StpUserUtil { // =================== 过期时间相关 =================== /** - * 获取当前登录者的token剩余有效时间 (单位: 秒) + * 获取当前登录者的 token 剩余有效时间 (单位: 秒) * @return token剩余有效时间 */ public static long getTokenTimeout() { @@ -362,7 +374,7 @@ public class StpUserUtil { } /** - * 获取当前登录者的Session剩余有效时间 (单位: 秒) + * 获取当前登录者的 User-Session 剩余有效时间 (单位: 秒) * @return token剩余有效时间 */ public static long getSessionTimeout() { @@ -370,7 +382,7 @@ public class StpUserUtil { } /** - * 获取当前token的专属Session剩余有效时间 (单位: 秒) + * 获取当前 Token-Session 剩余有效时间 (单位: 秒) * @return token剩余有效时间 */ public static long getTokenSessionTimeout() { @@ -378,8 +390,8 @@ public class StpUserUtil { } /** - * 获取当前token[临时过期]剩余有效时间 (单位: 秒) - * @return token[临时过期]剩余有效时间 + * 获取当前 token [临时过期] 剩余有效时间 (单位: 秒) + * @return token [临时过期] 剩余有效时间 */ public static long getTokenActivityTimeout() { return stpLogic.getTokenActivityTimeout(); @@ -390,7 +402,7 @@ public class StpUserUtil { // =================== 角色验证操作 =================== /** - * 指定账号id是否含有角色标识, 返回true或false + * 判断:指定账号id是否含有角色标识, 返回true或false * @param loginId 账号id * @param role 角色标识 * @return 是否含有指定角色标识 @@ -400,16 +412,34 @@ public class StpUserUtil { } /** - * 当前账号是否含有指定角色标识, 返回true或false + * 判断:当前账号是否含有指定角色标识, 返回true或false * @param role 角色标识 * @return 是否含有指定角色标识 */ public static boolean hasRole(String role) { return stpLogic.hasRole(role); } - + /** - * 当前账号是否含有指定角色标识, 如果验证未通过,则抛出异常: NotRoleException + * 判断:当前账号是否含有指定角色标识 [指定多个,必须全部验证通过] + * @param roleArray 角色标识数组 + * @return true或false + */ + public static boolean hasRoleAnd(String... roleArray){ + return stpLogic.hasRoleAnd(roleArray); + } + + /** + * 判断:当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可] + * @param roleArray 角色标识数组 + * @return true或false + */ + public static boolean hasRoleOr(String... roleArray){ + return stpLogic.hasRoleOr(roleArray); + } + + /** + * 校验:当前账号是否含有指定角色标识, 如果验证未通过,则抛出异常: NotRoleException * @param role 角色标识 */ public static void checkRole(String role) { @@ -417,7 +447,7 @@ public class StpUserUtil { } /** - * 当前账号是否含有指定角色标识 [指定多个,必须全部验证通过] + * 校验:当前账号是否含有指定角色标识 [指定多个,必须全部验证通过] * @param roleArray 角色标识数组 */ public static void checkRoleAnd(String... roleArray){ @@ -425,18 +455,27 @@ public class StpUserUtil { } /** - * 当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可] + * 校验:当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可] * @param roleArray 角色标识数组 */ public static void checkRoleOr(String... roleArray){ stpLogic.checkRoleOr(roleArray); } - + + // -- + /** + * 返回当前账号所拥有的角色标识集合 + * @return / + */ + public static List getRoleList() { + return stpLogic.getRoleList(); + } + // =================== 权限验证操作 =================== /** - * 指定账号id是否含有指定权限, 返回true或false + * 判断:指定账号id是否含有指定权限, 返回true或false * @param loginId 账号id * @param permission 权限码 * @return 是否含有指定权限 @@ -446,7 +485,7 @@ public class StpUserUtil { } /** - * 当前账号是否含有指定权限, 返回true或false + * 判断:当前账号是否含有指定权限, 返回true或false * @param permission 权限码 * @return 是否含有指定权限 */ @@ -455,7 +494,25 @@ public class StpUserUtil { } /** - * 当前账号是否含有指定权限, 如果验证未通过,则抛出异常: NotPermissionException + * 判断:当前账号是否含有指定权限, [指定多个,必须全部具有] + * @param permissionArray 权限码数组 + * @return true 或 false + */ + public static boolean hasPermissionAnd(String... permissionArray){ + return stpLogic.hasPermissionAnd(permissionArray); + } + + /** + * 判断:当前账号是否含有指定权限 [指定多个,只要其一验证通过即可] + * @param permissionArray 权限码数组 + * @return true 或 false + */ + public static boolean hasPermissionOr(String... permissionArray){ + return stpLogic.hasPermissionOr(permissionArray); + } + + /** + * 校验:当前账号是否含有指定权限, 如果验证未通过,则抛出异常: NotPermissionException * @param permission 权限码 */ public static void checkPermission(String permission) { @@ -463,7 +520,7 @@ public class StpUserUtil { } /** - * 当前账号是否含有指定权限 [指定多个,必须全部验证通过] + * 校验:当前账号是否含有指定权限 [指定多个,必须全部验证通过] * @param permissionArray 权限码数组 */ public static void checkPermissionAnd(String... permissionArray) { @@ -471,13 +528,22 @@ public class StpUserUtil { } /** - * 当前账号是否含有指定权限 [指定多个,只要其一验证通过即可] + * 校验:当前账号是否含有指定权限 [指定多个,只要其一验证通过即可] * @param permissionArray 权限码数组 */ public static void checkPermissionOr(String... permissionArray) { stpLogic.checkPermissionOr(permissionArray); } + // -- + /** + * 返回当前账号所拥有的权限码集合 + * @return / + */ + public static List getPermissionList() { + return stpLogic.getPermissionList(); + } + // =================== id 反查token 相关操作 =================== @@ -687,6 +753,7 @@ public class StpUserUtil { /** *

本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.getLoginType() ,使用方式保持不变

+ * * 获取当前StpLogin的loginKey * @return 当前StpLogin的loginKey */ @@ -697,6 +764,7 @@ public class StpUserUtil { /** *

本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变

+ * * 在当前会话上登录id * @param loginId 登录id,建议的类型:(long | int | String) */ @@ -707,6 +775,7 @@ public class StpUserUtil { /** *

本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变

+ * * 在当前会话上登录id, 并指定登录设备 * @param loginId 登录id,建议的类型:(long | int | String) * @param device 设备标识 @@ -718,6 +787,7 @@ public class StpUserUtil { /** *

本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变

+ * * 在当前会话上登录id, 并指定登录设备 * @param loginId 登录id,建议的类型:(long | int | String) * @param isLastingCookie 是否为持久Cookie @@ -729,6 +799,7 @@ public class StpUserUtil { /** *

本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变

+ * * 在当前会话上登录id, 并指定所有登录参数Model * @param loginId 登录id,建议的类型:(long | int | String) * @param loginModel 此次登录的参数Model @@ -738,4 +809,27 @@ public class StpUserUtil { stpLogic.login(loginId, loginModel); } + /** + *

本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.kickout() ,使用方式保持不变

+ * + * 会话注销,根据账号id (踢人下线) + *

当对方再次访问系统时,会抛出NotLoginException异常,场景值=-2 + * @param loginId 账号id + */ + public static void logoutByLoginId(Object loginId) { + stpLogic.kickout(loginId); + } + + /** + *

本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.kickout() ,使用方式保持不变

+ * + * 会话注销,根据账号id and 设备标识 (踢人下线) + *

当对方再次访问系统时,会抛出NotLoginException异常,场景值=-2

+ * @param loginId 账号id + * @param device 设备标识 (填null代表所有注销设备) + */ + public static void logoutByLoginId(Object loginId, String device) { + stpLogic.kickout(loginId, device); + } + } diff --git a/sa-token-demo/sa-token-demo-thymeleaf/.gitignore b/sa-token-demo/sa-token-demo-thymeleaf/.gitignore new file mode 100644 index 00000000..99a6e767 --- /dev/null +++ b/sa-token-demo/sa-token-demo-thymeleaf/.gitignore @@ -0,0 +1,12 @@ +target/ + +node_modules/ +bin/ +.settings/ +unpackage/ +.classpath +.project + +.idea/ + +.factorypath \ No newline at end of file diff --git a/sa-token-demo/sa-token-demo-thymeleaf/pom.xml b/sa-token-demo/sa-token-demo-thymeleaf/pom.xml new file mode 100644 index 00000000..a576aa2b --- /dev/null +++ b/sa-token-demo/sa-token-demo-thymeleaf/pom.xml @@ -0,0 +1,71 @@ + + 4.0.0 + cn.dev33 + sa-token-demo-thymeleaf + 0.0.1-SNAPSHOT + + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.RELEASE + + + + + + 1.26.0 + + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-aop + + + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + + + cn.dev33 + sa-token-spring-boot-starter + ${sa-token-version} + + + + + cn.dev33 + sa-token-dialect-thymeleaf + ${sa-token-version} + + + + + org.springframework.boot + spring-boot-devtools + provided + + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + + + diff --git a/sa-token-demo/sa-token-demo-thymeleaf/src/main/java/com/pj/SaTokenThymeleafDemoApplication.java b/sa-token-demo/sa-token-demo-thymeleaf/src/main/java/com/pj/SaTokenThymeleafDemoApplication.java new file mode 100644 index 00000000..bcd9cd24 --- /dev/null +++ b/sa-token-demo/sa-token-demo-thymeleaf/src/main/java/com/pj/SaTokenThymeleafDemoApplication.java @@ -0,0 +1,16 @@ +package com.pj; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import cn.dev33.satoken.SaManager; + +@SpringBootApplication +public class SaTokenThymeleafDemoApplication { + + public static void main(String[] args) { + SpringApplication.run(SaTokenThymeleafDemoApplication.class, args); + System.out.println("\n启动成功:sa-token配置如下:" + SaManager.getConfig()); + } + +} \ No newline at end of file diff --git a/sa-token-demo/sa-token-demo-thymeleaf/src/main/java/com/pj/satoken/SaTokenConfigure.java b/sa-token-demo/sa-token-demo-thymeleaf/src/main/java/com/pj/satoken/SaTokenConfigure.java new file mode 100644 index 00000000..c0cce23d --- /dev/null +++ b/sa-token-demo/sa-token-demo-thymeleaf/src/main/java/com/pj/satoken/SaTokenConfigure.java @@ -0,0 +1,33 @@ +package com.pj.satoken; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.thymeleaf.spring5.view.ThymeleafViewResolver; + +import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.thymeleaf.dialect.SaTokenDialect; + + +/** + * [Sa-Token 权限认证] 配置类 + * @author kong + * + */ +@Configuration +public class SaTokenConfigure implements WebMvcConfigurer { + + // Sa-Token 标签方言 (Thymeleaf版) + @Bean + public SaTokenDialect getSaTokenDialect() { + return new SaTokenDialect(); + } + + // 为 Thymeleaf 注入全局变量,以便在页面中调用 Sa-Token 的方法 + @Autowired + private void configureThymeleafStaticVars(ThymeleafViewResolver viewResolver) { + viewResolver.addStaticVariable("stp", StpUtil.stpLogic); + } + +} diff --git a/sa-token-demo/sa-token-demo-thymeleaf/src/main/java/com/pj/satoken/StpInterfaceImpl.java b/sa-token-demo/sa-token-demo-thymeleaf/src/main/java/com/pj/satoken/StpInterfaceImpl.java new file mode 100644 index 00000000..b6cc79f7 --- /dev/null +++ b/sa-token-demo/sa-token-demo-thymeleaf/src/main/java/com/pj/satoken/StpInterfaceImpl.java @@ -0,0 +1,44 @@ +package com.pj.satoken; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.stereotype.Component; + +import cn.dev33.satoken.stp.StpInterface; + +/** + * 自定义权限验证接口扩展 + */ +@Component // 打开此注解,保证此类被springboot扫描,即可完成sa-token的自定义权限验证扩展 +public class StpInterfaceImpl implements StpInterface { + + /** + * 返回一个账号所拥有的权限码集合 + */ + @Override + public List getPermissionList(Object loginId, String loginType) { + // 本list仅做模拟,实际项目中要根据具体业务逻辑来查询权限 + List list = new ArrayList(); + list.add("101"); + list.add("user-add"); + list.add("user-delete"); + list.add("user-update"); + list.add("user-get"); + list.add("article-get"); + return list; + } + + /** + * 返回一个账号所拥有的角色标识集合 + */ + @Override + public List getRoleList(Object loginId, String loginType) { + // 本list仅做模拟,实际项目中要根据具体业务逻辑来查询角色 + List list = new ArrayList(); + list.add("admin"); + list.add("super-admin"); + return list; + } + +} diff --git a/sa-token-demo/sa-token-demo-thymeleaf/src/main/java/com/pj/test/GlobalException.java b/sa-token-demo/sa-token-demo-thymeleaf/src/main/java/com/pj/test/GlobalException.java new file mode 100644 index 00000000..4992d3d1 --- /dev/null +++ b/sa-token-demo/sa-token-demo-thymeleaf/src/main/java/com/pj/test/GlobalException.java @@ -0,0 +1,24 @@ +package com.pj.test; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import cn.dev33.satoken.util.SaResult; + +/** + * 全局异常处理 + */ +@RestControllerAdvice +public class GlobalException { + + // 全局异常拦截(拦截项目中的所有异常) + @ExceptionHandler + public SaResult handlerException(Exception e, HttpServletRequest request, HttpServletResponse response) throws Exception { + e.printStackTrace(); + return SaResult.error(e.getMessage()); + } + +} diff --git a/sa-token-demo/sa-token-demo-thymeleaf/src/main/java/com/pj/test/TestController.java b/sa-token-demo/sa-token-demo-thymeleaf/src/main/java/com/pj/test/TestController.java new file mode 100644 index 00000000..ad2f4a8d --- /dev/null +++ b/sa-token-demo/sa-token-demo-thymeleaf/src/main/java/com/pj/test/TestController.java @@ -0,0 +1,40 @@ +package com.pj.test; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.ModelAndView; + +import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.util.SaResult; + +/** + * Sa-Token-SSO Server端 Controller + * @author kong + * + */ +@RestController +public class TestController { + + // 首页 + @RequestMapping("/") + public Object index() { + return new ModelAndView("index.html"); + } + + // 登录 + @RequestMapping("login") + public SaResult login(@RequestParam(defaultValue="10001") String id) { + StpUtil.login(id); + StpUtil.getSession().set("name", "zhangsan"); + return SaResult.ok(); + } + + // 注销 + @RequestMapping("logout") + public SaResult logout() { + StpUtil.logout(); + return SaResult.ok(); + } + +} diff --git a/sa-token-demo/sa-token-demo-thymeleaf/src/main/resources/application.yml b/sa-token-demo/sa-token-demo-thymeleaf/src/main/resources/application.yml new file mode 100644 index 00000000..14a193d4 --- /dev/null +++ b/sa-token-demo/sa-token-demo-thymeleaf/src/main/resources/application.yml @@ -0,0 +1,43 @@ +# 端口 +server: + port: 8081 + +# sa-token配置 +sa-token: + # token名称 (同时也是cookie名称) + token-name: satoken + # token有效期,单位s 默认30天, -1代表永不过期 + timeout: 2592000 + # token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒 + activity-timeout: -1 + # 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录) + is-concurrent: true + # 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token) + is-share: true + # token风格 + token-style: uuid +spring: + # redis配置 + redis: + # Redis数据库索引(默认为0) + database: 0 + # Redis服务器地址 + host: 127.0.0.1 + # Redis服务器连接端口 + port: 6379 + # Redis服务器连接密码(默认为空) + password: + # 连接超时时间(毫秒) + timeout: 10000ms + lettuce: + pool: + # 连接池最大连接数 + max-active: 200 + # 连接池最大阻塞等待时间(使用负值表示没有限制) + max-wait: -1ms + # 连接池中的最大空闲连接 + max-idle: 10 + # 连接池中的最小空闲连接 + min-idle: 0 + + \ No newline at end of file diff --git a/sa-token-demo/sa-token-demo-thymeleaf/src/main/resources/templates/index.html b/sa-token-demo/sa-token-demo-thymeleaf/src/main/resources/templates/index.html new file mode 100644 index 00000000..97b3addc --- /dev/null +++ b/sa-token-demo/sa-token-demo-thymeleaf/src/main/resources/templates/index.html @@ -0,0 +1,36 @@ + + + + Sa-Token 集成 Thymeleaf 标签方言 + + + + +
+

Sa-Token 集成 Thymeleaf 标签方言 —— 测试页面

+

+ 登录 + 注销 +

+ +

登录之后才能显示:value

+

不登录之后才能显示:value

+ +

具有角色 admin 才能显示:value

+

同时具备多个角色才能显示:value

+

只要具有其中一个角色就能显示:value

+

不具有角色 admin 才能显示:value

+ +

具有权限 user-add 才能显示:value

+

同时具备多个权限才能显示:value

+

只要具有其中一个权限就能显示:value

+

不具有权限 user-add 才能显示:value

+ +

+ 从SaSession中取值: + +

+ +
+ + diff --git a/sa-token-doc/doc/up/safe-auth.md b/sa-token-doc/doc/up/safe-auth.md index b244a992..be3e12ca 100644 --- a/sa-token-doc/doc/up/safe-auth.md +++ b/sa-token-doc/doc/up/safe-auth.md @@ -2,7 +2,7 @@ 在某些敏感操作下,我们需要对已登录的会话进行二次验证 -比如 Gitee 的仓库删除操作,虽然我们已经登录了账号,当我们点击 **[删除]** 按钮时,还是需要再次输入一遍密码,这么做主要为了两点: +比如代码托管平台的仓库删除操作,尽管我们已经登录了账号,当我们点击 **[删除]** 按钮时,还是需要再次输入一遍密码,这么做主要为了两点: 1. 保证操作者是当前账号本人 2. 增加操作步骤,防止误删除重要数据 diff --git a/sa-token-plugin/pom.xml b/sa-token-plugin/pom.xml index 5600d1f6..b632e9a2 100644 --- a/sa-token-plugin/pom.xml +++ b/sa-token-plugin/pom.xml @@ -20,6 +20,7 @@ sa-token-alone-redis sa-token-dao-redis sa-token-dao-redis-jackson + sa-token-dialect-thymeleaf sa-token-oauth2 sa-token-quick-login sa-token-spring-aop diff --git a/sa-token-plugin/sa-token-dialect-thymeleaf/.gitignore b/sa-token-plugin/sa-token-dialect-thymeleaf/.gitignore new file mode 100644 index 00000000..f56feec7 --- /dev/null +++ b/sa-token-plugin/sa-token-dialect-thymeleaf/.gitignore @@ -0,0 +1,12 @@ +target/ + +node_modules/ +bin/ +.settings/ +unpackage/ +.classpath +.project + +.factorypath + +.idea/ \ No newline at end of file diff --git a/sa-token-plugin/sa-token-dialect-thymeleaf/pom.xml b/sa-token-plugin/sa-token-dialect-thymeleaf/pom.xml new file mode 100644 index 00000000..d556f0de --- /dev/null +++ b/sa-token-plugin/sa-token-dialect-thymeleaf/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + + cn.dev33 + sa-token-plugin + 1.26.0 + + jar + + sa-token-dialect-thymeleaf + sa-token-dialect-thymeleaf + sa-token-dialect-thymeleaf + + + + + cn.dev33 + sa-token-core + ${sa-token-version} + + + + org.thymeleaf + thymeleaf + 3.0.9.RELEASE + true + + + + org.springframework.boot + spring-boot-configuration-processor + 2.0.0.RELEASE + true + + + + diff --git a/sa-token-plugin/sa-token-dialect-thymeleaf/src/main/java/cn/dev33/satoken/thymeleaf/dialect/SaTokenDialect.java b/sa-token-plugin/sa-token-dialect-thymeleaf/src/main/java/cn/dev33/satoken/thymeleaf/dialect/SaTokenDialect.java new file mode 100644 index 00000000..a1100368 --- /dev/null +++ b/sa-token-plugin/sa-token-dialect-thymeleaf/src/main/java/cn/dev33/satoken/thymeleaf/dialect/SaTokenDialect.java @@ -0,0 +1,82 @@ +package cn.dev33.satoken.thymeleaf.dialect; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.thymeleaf.dialect.AbstractProcessorDialect; +import org.thymeleaf.processor.IProcessor; + +import cn.dev33.satoken.stp.StpLogic; +import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.util.SaFoxUtil; + +/** + * Sa-Token 集成 Thymeleaf 标签方言 + * + * @author kong + * + */ +public class SaTokenDialect extends AbstractProcessorDialect { + + /** + * 底层使用的 StpLogic + */ + public StpLogic stpLogic; + + /** + * 使用默认参数注册方言 + */ + public SaTokenDialect() { + this("sa", 1000, StpUtil.stpLogic); + } + + /** + * 构造方言对象,使用 + * @param name 方言名称 + * @param recedence 优先级 + * @param stpLogic 使用的 StpLogic 对象 + */ + public SaTokenDialect(String name, int recedence, StpLogic stpLogic) { + // 名称、前缀、优先级 + super(name, name, recedence); + this.stpLogic = stpLogic; + } + + /** + * 返回所有方言处理器 + */ + @Override + public Set getProcessors(String prefix) { + return new HashSet(Arrays.asList( + // 登录判断 + new SaTokenTagProcessor(prefix, "login", value -> stpLogic.isLogin()), + new SaTokenTagProcessor(prefix, "notLogin", value -> stpLogic.isLogin() == false), + + // 角色判断 + new SaTokenTagProcessor(prefix, "hasRole", value -> stpLogic.hasRole(value)), + new SaTokenTagProcessor(prefix, "hasRoleOr", value -> stpLogic.hasRoleOr(toArray(value))), + new SaTokenTagProcessor(prefix, "hasRoleAnd", value -> stpLogic.hasRoleAnd(toArray(value))), + new SaTokenTagProcessor(prefix, "lackRole", value -> stpLogic.hasRole(value) == false), + + // 权限判断 + new SaTokenTagProcessor(prefix, "hasPermission", value -> stpLogic.hasPermission(value)), + new SaTokenTagProcessor(prefix, "hasPermissionOr", value -> stpLogic.hasPermissionOr(toArray(value))), + new SaTokenTagProcessor(prefix, "hasPermissionAnd", value -> stpLogic.hasPermissionAnd(toArray(value))), + new SaTokenTagProcessor(prefix, "lackPermission", value -> stpLogic.hasPermission(value) == false) + + )); + } + + /** + * String 转 Array + * @param str 字符串 + * @return 数组 + */ + public String[] toArray(String str) { + List list = SaFoxUtil.convertStringToList(str); + return list.toArray(new String[list.size()]); + } + +} diff --git a/sa-token-plugin/sa-token-dialect-thymeleaf/src/main/java/cn/dev33/satoken/thymeleaf/dialect/SaTokenTagProcessor.java b/sa-token-plugin/sa-token-dialect-thymeleaf/src/main/java/cn/dev33/satoken/thymeleaf/dialect/SaTokenTagProcessor.java new file mode 100644 index 00000000..2841d10f --- /dev/null +++ b/sa-token-plugin/sa-token-dialect-thymeleaf/src/main/java/cn/dev33/satoken/thymeleaf/dialect/SaTokenTagProcessor.java @@ -0,0 +1,45 @@ +package cn.dev33.satoken.thymeleaf.dialect; + +import java.util.function.Function; + +import org.thymeleaf.context.ITemplateContext; +import org.thymeleaf.engine.AttributeName; +import org.thymeleaf.model.IProcessableElementTag; +import org.thymeleaf.processor.element.AbstractAttributeTagProcessor; +import org.thymeleaf.processor.element.IElementTagStructureHandler; +import org.thymeleaf.templatemode.TemplateMode; + +/** + * 封装 Sa-Token 标签方言处理器 + * @author kong + * + */ +public class SaTokenTagProcessor extends AbstractAttributeTagProcessor { + + Function fun; + + public SaTokenTagProcessor(final String dialectPrefix, String arrtName, Function fun) { + super( + TemplateMode.HTML, // This processor will apply only to HTML mode + dialectPrefix, // Prefix to be applied to name for matching + null, // No tag name: match any tag name + false, // No prefix to be applied to tag name + arrtName, // Name of the attribute that will be matched + true, // Apply dialect prefix to attribute name + 10000, // Precedence (inside dialect's own precedence) + true); // Remove the matched attribute afterwards + this.fun = fun; + } + + @Override + protected void doProcess( + final ITemplateContext context, final IProcessableElementTag tag, + final AttributeName attributeName, final String attributeValue, + final IElementTagStructureHandler structureHandler) { + // 执行表达式返回值为false,则删除这个标签 + if(this.fun.apply(attributeValue) == false) { + structureHandler.removeElement(); + }; + } + +} \ No newline at end of file diff --git a/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/spring/SaBeanInject.java b/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/spring/SaBeanInject.java index 75751c80..cd019da6 100644 --- a/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/spring/SaBeanInject.java +++ b/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/spring/SaBeanInject.java @@ -16,6 +16,8 @@ import cn.dev33.satoken.listener.SaTokenListener; import cn.dev33.satoken.sso.SaSsoTemplate; import cn.dev33.satoken.sso.SaSsoUtil; import cn.dev33.satoken.stp.StpInterface; +import cn.dev33.satoken.stp.StpLogic; +import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.temp.SaTempInterface; /** @@ -125,6 +127,15 @@ public class SaBeanInject { public void setSaSsoTemplate(SaSsoTemplate saSsoTemplate) { SaSsoUtil.saSsoTemplate = saSsoTemplate; } + + /** + * 注入自定义的 StpLogic + * @param stpLogic / + */ + @Autowired(required = false) + public void setStpLogic(StpLogic stpLogic) { + StpUtil.setStpLogic(stpLogic); + } /** * 利用自动注入特性,获取Spring框架内部使用的路由匹配器 @@ -137,5 +148,4 @@ public class SaBeanInject { SaPathMatcherHolder.setPathMatcher(pathMatcher); } - } diff --git a/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/XPluginImp.java b/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/XPluginImp.java index e665ff01..d5de9b7f 100644 --- a/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/XPluginImp.java +++ b/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/XPluginImp.java @@ -7,6 +7,7 @@ import org.noear.solon.core.Plugin; import cn.dev33.satoken.SaManager; import cn.dev33.satoken.action.SaTokenAction; +import cn.dev33.satoken.annotation.SaCheckBasic; import cn.dev33.satoken.annotation.SaCheckLogin; import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.annotation.SaCheckRole; @@ -23,6 +24,8 @@ import cn.dev33.satoken.solon.integration.SaTokenMethodInterceptor; import cn.dev33.satoken.sso.SaSsoTemplate; import cn.dev33.satoken.sso.SaSsoUtil; import cn.dev33.satoken.stp.StpInterface; +import cn.dev33.satoken.stp.StpLogic; +import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.temp.SaTempInterface; /** @@ -38,6 +41,7 @@ public class XPluginImp implements Plugin { Aop.context().beanAroundAdd(SaCheckRole.class, SaTokenMethodInterceptor.INSTANCE); Aop.context().beanAroundAdd(SaCheckLogin.class, SaTokenMethodInterceptor.INSTANCE); Aop.context().beanAroundAdd(SaCheckSafe.class, SaTokenMethodInterceptor.INSTANCE); + Aop.context().beanAroundAdd(SaCheckBasic.class, SaTokenMethodInterceptor.INSTANCE); //集成初始化 @@ -87,6 +91,11 @@ public class XPluginImp implements Plugin { Aop.getAsyn(SaSsoTemplate.class, bw->{ SaSsoUtil.saSsoTemplate = bw.raw(); }); + + // 自定义 StpLogic 对象 + Aop.getAsyn(StpLogic.class, bw->{ + StpUtil.setStpLogic(bw.raw()); + }); } diff --git a/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/SaBeanInject.java b/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/SaBeanInject.java index 7390a52c..a04501ee 100644 --- a/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/SaBeanInject.java +++ b/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/SaBeanInject.java @@ -16,6 +16,8 @@ import cn.dev33.satoken.listener.SaTokenListener; import cn.dev33.satoken.sso.SaSsoTemplate; import cn.dev33.satoken.sso.SaSsoUtil; import cn.dev33.satoken.stp.StpInterface; +import cn.dev33.satoken.stp.StpLogic; +import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.temp.SaTempInterface; /** @@ -125,6 +127,15 @@ public class SaBeanInject { public void setSaSsoTemplate(SaSsoTemplate saSsoTemplate) { SaSsoUtil.saSsoTemplate = saSsoTemplate; } + + /** + * 注入自定义的 StpLogic + * @param stpLogic / + */ + @Autowired(required = false) + public void setStpLogic(StpLogic stpLogic) { + StpUtil.setStpLogic(stpLogic); + } /** * 利用自动注入特性,获取Spring框架内部使用的路由匹配器