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 f3989153..144c157d 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 @@ -5,6 +5,8 @@ import cn.dev33.satoken.config.SaTokenConfig; import cn.dev33.satoken.dao.SaTokenDao; import cn.dev33.satoken.util.SaTokenConsts; +import java.util.Map; + /** * 调用 `StpUtil.login()` 时的 [配置参数 Model ] * @author kong @@ -27,6 +29,11 @@ public class SaLoginModel { */ public Long timeout; + /** + * jwt扩展信息 + */ + public Map expandInfoMap; + /** * @return 参考 {@link #device} @@ -76,6 +83,21 @@ public class SaLoginModel { return this; } + /** + * @return 参考 {@link #expandInfoMap} + */ + public Map getExpandInfoMap() { + return expandInfoMap; + } + + /** + * @param expandInfoMap 参考 {@link #expandInfoMap} + * @return 对象自身 + */ + public SaLoginModel setExpandInfoMap(Map expandInfoMap) { + this.expandInfoMap = expandInfoMap; + return this; + } /** * @return Cookie时长 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 33312b0c..362ccf43 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 @@ -1,9 +1,6 @@ package cn.dev33.satoken.stp; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Objects; +import java.util.*; import java.util.function.Consumer; import cn.dev33.satoken.SaManager; @@ -94,6 +91,18 @@ public class StpLogic { return SaStrategy.me.createToken.apply(loginId, loginType); } + /** + * 创建一个TokenValue + * @param loginId loginId + * @param device 设备标识 + * @param expandInfoMap 扩展信息 + * @param timeout 过期时间 + * @return 生成的tokenValue + */ + public String createTokenValue(Object loginId, String device, Map expandInfoMap, long timeout) { + return SaStrategy.me.createToken.apply(loginId, loginType); + } + /** * 在当前会话写入当前TokenValue * @param tokenValue token值 @@ -260,6 +269,15 @@ public class StpLogic { login(id, new SaLoginModel().setDevice(device)); } + /** + * 会话登录,并指定扩展信息 for Jwt + * @param id 账号id,建议的类型:(long | int | String) + * @param expandInfoMap 扩展数据 + */ + public void login(Object id, Map expandInfoMap) { + login(id, new SaLoginModel().setExpandInfoMap(expandInfoMap)); + } + /** * 会话登录,并指定是否 [记住我] * @param id 账号id,建议的类型:(long | int | String) @@ -301,7 +319,7 @@ public class StpLogic { } // 如果至此,仍未成功创建tokenValue, 则开始生成一个 if(tokenValue == null) { - tokenValue = createTokenValue(id, loginModel.getDeviceOrDefault(), loginModel.getTimeout()); + tokenValue = createTokenValue(id, loginModel.getDeviceOrDefault(), loginModel.getExpandInfoMap(), loginModel.getTimeout()); } // ------ 3. 获取 User-Session , 续期 @@ -659,6 +677,18 @@ public class StpLogic { } return getLoginIdNotHandle(tokenValue); } + + /** + * 获取指定Token对应的扩展信息,如果未登录,则返回 null + * @param tokenValue token + * @return 账号id + */ + public Object getExpandInfoByToken(String tokenValue) { + if(tokenValue == null) { + return null; + } + return getExpandInfoNotHandle(tokenValue); + } /** * 获取指定Token对应的账号id (不做任何特殊处理) @@ -668,6 +698,17 @@ public class StpLogic { public String getLoginIdNotHandle(String tokenValue) { return getSaTokenDao().get(splicingKeyTokenValue(tokenValue)); } + + /** + * 获取指定Token对应的扩展信息 (不做任何特殊处理) + * @param tokenValue token值 + * @return 账号id + */ + public Object getExpandInfoNotHandle(String tokenValue) { + return getSaTokenDao().get(splicingKeyTokenValue(tokenValue)); + } + + // ---- 其它操作 /** 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 2f46bc22..8c76e64b 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 @@ -1,6 +1,7 @@ package cn.dev33.satoken.stp; import java.util.List; +import java.util.Map; import cn.dev33.satoken.SaManager; import cn.dev33.satoken.fun.SaFunction; @@ -114,6 +115,15 @@ public class StpUtil { stpLogic.login(id, device); } + /** + * 会话登录,并指定登录设备 + * @param id 账号id,建议的类型:(long | int | String) + * @param expandInfoMap 扩展信息 + */ + public static void login(Object id, Map expandInfoMap) { + stpLogic.login(id, expandInfoMap); + } + /** * 会话登录,并指定是否 [记住我] * @param id 账号id,建议的类型:(long | int | String) @@ -286,6 +296,15 @@ public class StpUtil { public static Object getLoginIdByToken(String tokenValue) { return stpLogic.getLoginIdByToken(tokenValue); } + + /** + * 获取指定Token对应的扩展数据,如果未登录,则返回 null + * @param tokenValue token + * @return 账号id + */ + public static Object getExpandInfoByToken(String tokenValue) { + return stpLogic.getExpandInfoByToken(tokenValue); + } // =================== User-Session 相关 =================== diff --git a/sa-token-plugin/sa-token-jwt/src/main/java/cn/dev33/satoken/jwt/SaJwtUtil.java b/sa-token-plugin/sa-token-jwt/src/main/java/cn/dev33/satoken/jwt/SaJwtUtil.java index bed4996f..7f19b001 100644 --- a/sa-token-plugin/sa-token-jwt/src/main/java/cn/dev33/satoken/jwt/SaJwtUtil.java +++ b/sa-token-plugin/sa-token-jwt/src/main/java/cn/dev33/satoken/jwt/SaJwtUtil.java @@ -4,10 +4,13 @@ import cn.dev33.satoken.dao.SaTokenDao; import cn.dev33.satoken.exception.NotLoginException; import cn.dev33.satoken.exception.SaTokenException; import cn.dev33.satoken.util.SaFoxUtil; +import cn.hutool.core.collection.CollectionUtil; import cn.hutool.json.JSONObject; import cn.hutool.jwt.JWT; import cn.hutool.jwt.JWTException; +import java.util.Map; + /** * jwt操作工具类封装 * @author kong @@ -33,7 +36,12 @@ public class SaJwtUtil { /** * key:有效截止期 (时间戳) */ - public static final String EFF = "eff"; + public static final String EFF = "eff"; + + /** + * key: 扩展数据 + */ + public static final String EXPAND = "expand"; /** * 当有效期被设为此值时,代表永不过期 @@ -97,6 +105,44 @@ public class SaJwtUtil { return token; } + /** + * 创建 jwt (全参数方式) + * @param loginType 账号类型 + * @param loginId 账号id + * @param device 设备标识 + * @param expandInfoMap 扩展数据 + * @param timeout token有效期 (单位 秒) + * @param keyt 秘钥 + * @return jwt-token + */ + public static String createToken(String loginType, Object loginId, String device, + Map expandInfoMap, long timeout, String keyt) { + + // 秘钥不可以为空 + SaTokenException.throwByNull(keyt, "请配置jwt秘钥"); + + // 计算有效期 + long effTime = timeout; + if(timeout != NEVER_EXPIRE) { + effTime = timeout * 1000 + System.currentTimeMillis(); + } + + JWT jwt = JWT.create() + .setPayload(LOGIN_TYPE, loginType) + .setPayload(LOGIN_ID, loginId) + .setPayload(DEVICE, device) + .setPayload(EFF, effTime); + + // 设定扩展数据 + if (CollectionUtil.isNotEmpty(expandInfoMap)) { + jwt.setPayload(EXPAND, expandInfoMap); + } + + // 返回 + return jwt.setKey(keyt.getBytes()).sign(); + } + + /** * jwt 解析(校验签名和密码) * @param token Jwt-Token值 @@ -188,6 +234,33 @@ public class SaJwtUtil { } } + /** + * getExpandMap + * + * @since 2021/11/23 5:05 下午 + * @param token token值 + * @param keyt 秘钥 + * @return 值 + */ + public static Object getExpandInfo(String token, String keyt) { + return getPayloads(token, keyt).get(EXPAND); + } + + /** + * 获取 jwt 代表的账号id (未登录时返回null) + * @param token Token值 + * @param keyt 秘钥 + * @return 值 + */ + public static Object getExpandInfoOrNull(String token, String keyt) { + try { + return getPayloads(token, keyt).get(EXPAND); + } catch (NotLoginException e) { + return null; + } + } + + /** * 获取 jwt 剩余有效期 * @param token JwtToken值 diff --git a/sa-token-plugin/sa-token-jwt/src/main/java/cn/dev33/satoken/jwt/StpLogicJwtForMix.java b/sa-token-plugin/sa-token-jwt/src/main/java/cn/dev33/satoken/jwt/StpLogicJwtForMix.java index c6db47b0..9c7e5e78 100644 --- a/sa-token-plugin/sa-token-jwt/src/main/java/cn/dev33/satoken/jwt/StpLogicJwtForMix.java +++ b/sa-token-plugin/sa-token-jwt/src/main/java/cn/dev33/satoken/jwt/StpLogicJwtForMix.java @@ -1,6 +1,7 @@ package cn.dev33.satoken.jwt; import java.util.List; +import java.util.Map; import cn.dev33.satoken.context.SaHolder; import cn.dev33.satoken.dao.SaTokenDao; @@ -54,6 +55,11 @@ public class StpLogicJwtForMix extends StpLogic { return SaJwtUtil.createToken(loginType, loginId, device, timeout, jwtSecretKey()); } + @Override + public String createTokenValue(Object loginId, String device, Map expandInfoMap, long timeout) { + return SaJwtUtil.createToken(loginType, loginId, device, expandInfoMap, timeout, jwtSecretKey()); + } + /** * 获取当前会话的Token信息 * @return token信息 @@ -95,6 +101,21 @@ public class StpLogicJwtForMix extends StpLogic { } } + @Override + public Object getExpandInfoNotHandle(String tokenValue) { + // 先验证 loginType,如果不符,则视为无效token,返回null + String loginType = SaJwtUtil.getPayloadsNotCheck(tokenValue, jwtSecretKey()).getStr(SaJwtUtil.LOGIN_TYPE); + if(getLoginType().equals(loginType) == false) { + return null; + } + // 获取 expandInfo + try { + return SaJwtUtil.getExpandInfo(tokenValue, jwtSecretKey()); + } catch (NotLoginException e) { + return null; + } + } + /** * 会话注销 */ diff --git a/sa-token-plugin/sa-token-jwt/src/main/java/cn/dev33/satoken/jwt/StpLogicJwtForStateless.java b/sa-token-plugin/sa-token-jwt/src/main/java/cn/dev33/satoken/jwt/StpLogicJwtForStateless.java index cbfb2760..d8d89eff 100644 --- a/sa-token-plugin/sa-token-jwt/src/main/java/cn/dev33/satoken/jwt/StpLogicJwtForStateless.java +++ b/sa-token-plugin/sa-token-jwt/src/main/java/cn/dev33/satoken/jwt/StpLogicJwtForStateless.java @@ -11,6 +11,8 @@ import cn.dev33.satoken.stp.SaTokenInfo; import cn.dev33.satoken.stp.StpLogic; import cn.dev33.satoken.stp.StpUtil; +import java.util.Map; + /** * Sa-Token 整合 jwt -- stateless 无状态 * @author kong @@ -55,6 +57,11 @@ public class StpLogicJwtForStateless extends StpLogic { return SaJwtUtil.createToken(loginType, loginId, device, timeout, jwtSecretKey()); } + @Override + public String createTokenValue(Object loginId, String device, Map expandInfoMap, long timeout) { + return SaJwtUtil.createToken(loginType, loginId, device, expandInfoMap, timeout, jwtSecretKey()); + } + /** * 获取当前会话的Token信息 * @return token信息 @@ -89,7 +96,7 @@ public class StpLogicJwtForStateless extends StpLogic { loginModel.build(getConfig()); // ------ 2、生成一个token - String tokenValue = createTokenValue(id, loginModel.getDeviceOrDefault(), loginModel.getTimeout()); + String tokenValue = createTokenValue(id, loginModel.getDeviceOrDefault(), loginModel.getExpandInfoMap(), loginModel.getTimeout()); // 3、在当前会话写入tokenValue setTokenValue(tokenValue, loginModel.getCookieTimeout()); @@ -117,6 +124,21 @@ public class StpLogicJwtForStateless extends StpLogic { } } + @Override + public Object getExpandInfoNotHandle(String tokenValue) { + // 先验证 loginType,如果不符,则视为无效token,返回null + String loginType = SaJwtUtil.getPayloadsNotCheck(tokenValue, jwtSecretKey()).getStr(SaJwtUtil.LOGIN_TYPE); + if(getLoginType().equals(loginType) == false) { + return null; + } + // 获取 expandInfoMap + try { + return SaJwtUtil.getExpandInfo(tokenValue, jwtSecretKey()); + } catch (NotLoginException e) { + return null; + } + } + /** * 会话注销 */