diff --git a/license.txt b/license.txt deleted file mode 100644 index 52d99a66..00000000 --- a/license.txt +++ /dev/null @@ -1,13 +0,0 @@ -Copyright 1999-2018 Alibaba Group Holding Ltd. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/sa-token-demo-springboot/.factorypath b/sa-token-demo-springboot/.factorypath index a207d798..70a2484b 100644 --- a/sa-token-demo-springboot/.factorypath +++ b/sa-token-demo-springboot/.factorypath @@ -36,7 +36,6 @@ - diff --git a/sa-token-demo-springboot/src/main/java/com/pj/satoken/SaTokenDaoRedis.java b/sa-token-demo-springboot/src/main/java/com/pj/satoken/SaTokenDaoRedis.java index f136e6a6..fac5b4b5 100644 --- a/sa-token-demo-springboot/src/main/java/com/pj/satoken/SaTokenDaoRedis.java +++ b/sa-token-demo-springboot/src/main/java/com/pj/satoken/SaTokenDaoRedis.java @@ -7,6 +7,7 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; +import org.springframework.stereotype.Component; import cn.dev33.satoken.dao.SaTokenDao; import cn.dev33.satoken.session.SaSession; @@ -14,7 +15,7 @@ import cn.dev33.satoken.session.SaSession; /** * sa-token持久层的实现类 , 基于redis */ -//@Component // 打开此注解,保证此类被springboot扫描,即可完成sa-token与redis的集成 +@Component // 打开此注解,保证此类被springboot扫描,即可完成sa-token与redis的集成 public class SaTokenDaoRedis implements SaTokenDao { @@ -42,14 +43,19 @@ public class SaTokenDaoRedis implements SaTokenDao { // 写入指定key-value键值对,并设定过期时间(单位:秒) @Override public void setValue(String key, String value, long timeout) { - stringRedisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS); + // 判断是否为永不过期 + if(timeout == SaTokenDao.NEVER_EXPIRE) { + stringRedisTemplate.opsForValue().set(key, value); + } else { + stringRedisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS); + } } // 更新指定key-value键值对 (过期时间取原来的值) @Override public void updateValue(String key, String value) { - long expire = redisTemplate.getExpire(key); - if(expire == -2) { // -2 = 无此键 + long expire = getTimeout(key); + if(expire == SaTokenDao.NOT_VALUE_EXPIRE) { // -2 = 无此键 return; } stringRedisTemplate.opsForValue().set(key, value, expire, TimeUnit.SECONDS); @@ -61,24 +67,36 @@ public class SaTokenDaoRedis implements SaTokenDao { stringRedisTemplate.delete(key); } + // 获取指定key的剩余存活时间 (单位: 秒) + @Override + public long getTimeout(String key) { + return stringRedisTemplate.getExpire(key); + } + + // 根据指定key的session,如果没有,则返回空 @Override - public SaSession getSaSession(String sessionId) { + public SaSession getSession(String sessionId) { return redisTemplate.opsForValue().get(sessionId); } // 将指定session持久化 @Override - public void saveSaSession(SaSession session, long timeout) { - redisTemplate.opsForValue().set(session.getId(), session, timeout, TimeUnit.SECONDS); + public void saveSession(SaSession session, long timeout) { + // 判断是否为永不过期 + if(timeout == SaTokenDao.NEVER_EXPIRE) { + redisTemplate.opsForValue().set(session.getId(), session); + } else { + redisTemplate.opsForValue().set(session.getId(), session, timeout, TimeUnit.SECONDS); + } } // 更新指定session @Override - public void updateSaSession(SaSession session) { - long expire = redisTemplate.getExpire(session.getId()); - if(expire == -2) { // -2 = 无此键 + public void updateSession(SaSession session) { + long expire = getSessionTimeout(session.getId()); + if(expire == SaTokenDao.NOT_VALUE_EXPIRE) { // -2 = 无此键 return; } redisTemplate.opsForValue().set(session.getId(), session, expire, TimeUnit.SECONDS); @@ -86,12 +104,22 @@ public class SaTokenDaoRedis implements SaTokenDao { // 删除一个指定的session @Override - public void deleteSaSession(String sessionId) { + public void deleteSession(String sessionId) { redisTemplate.delete(sessionId); } + // 获取指定SaSession的剩余存活时间 (单位: 秒) + + + @Override + public long getSessionTimeout(String sessionId) { + return redisTemplate.getExpire(sessionId); + } + + + diff --git a/sa-token-demo-springboot/src/main/java/com/pj/satoken/StpUserUtil.java b/sa-token-demo-springboot/src/main/java/com/pj/satoken/StpUserUtil.java index 537a614a..759f544d 100644 --- a/sa-token-demo-springboot/src/main/java/com/pj/satoken/StpUserUtil.java +++ b/sa-token-demo-springboot/src/main/java/com/pj/satoken/StpUserUtil.java @@ -57,7 +57,7 @@ public class StpUserUtil { * 获取当前会话的token信息:tokenName与tokenValue * @return 一个Map对象 */ - public static Map getTokenInfo() { + public static Map getTokenInfo() { return stpLogic.getTokenInfo(); } 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 2d831b72..6821cc5b 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 @@ -18,6 +18,16 @@ import cn.dev33.satoken.stp.StpUtil; @RequestMapping("/test/") public class TestController { + + // 当前是否登录 , 浏览器访问: http://localhost:8081/test/isLogin + @RequestMapping("isLogin") + public AjaxJson isLogin() { + System.out.println("当前是否登录:" + StpUtil.isLogin()); + System.out.println("当前登录账号id:" + StpUtil.getLoginId(-1)); + return AjaxJson.getSuccessData(StpUtil.getTokenInfo()); + } + + // 测试登录接口, 浏览器访问: http://localhost:8081/test/login @RequestMapping("login") public AjaxJson login(@RequestParam(defaultValue="10001") String id) { diff --git a/sa-token-demo-springboot/src/main/resources/application.yml b/sa-token-demo-springboot/src/main/resources/application.yml index c325d680..511bc472 100644 --- a/sa-token-demo-springboot/src/main/resources/application.yml +++ b/sa-token-demo-springboot/src/main/resources/application.yml @@ -7,8 +7,9 @@ spring: sa-token: # token名称 (同时也是cookie名称) token-name: satoken - # token有效期,单位s 默认30天 - timeout: 2592000 + # token有效期,单位s 默认30天, -1代表永不过期 +# timeout: 2592000 + timeout: -1 # 在多人登录同一账号时,是否共享会话 (为true时共用一个,为false时新登录挤掉旧登录) is-share: true # 是否尝试从请求体里读取token diff --git a/sp-token-core/src/main/java/cn/dev33/satoken/dao/SaTokenDao.java b/sp-token-core/src/main/java/cn/dev33/satoken/dao/SaTokenDao.java index 2de1ce57..eb6503f9 100644 --- a/sp-token-core/src/main/java/cn/dev33/satoken/dao/SaTokenDao.java +++ b/sp-token-core/src/main/java/cn/dev33/satoken/dao/SaTokenDao.java @@ -10,6 +10,14 @@ import cn.dev33.satoken.session.SaSession; public interface SaTokenDao { + /** 常量,表示一个key永不过期 (在一个key被标注为永远不过期时返回此值) */ + public static final Long NEVER_EXPIRE = -1L; + + /** 常量,表示系统中不存在这个缓存 (在对不存在的key获取剩余存活时间时返回此值) */ + public static final Long NOT_VALUE_EXPIRE = -2L; + + + /** * 根据key获取value ,如果没有,则返回空 * @param key 键名称 @@ -38,6 +46,12 @@ public interface SaTokenDao { */ public void delKey(String key); + /** + * 获取指定key的剩余存活时间 (单位: 秒) + * @param key 指定key + * @return 这个key的剩余存活时间 + */ + public long getTimeout(String key); /** @@ -45,26 +59,36 @@ public interface SaTokenDao { * @param sessionId 键名称 * @return SaSession */ - public SaSession getSaSession(String sessionId); + public SaSession getSession(String sessionId); /** - * 将指定session持久化 + * 将指定session持久化 * @param session 要保存的session对象 * @param timeout 过期时间,单位: s */ - public void saveSaSession(SaSession session, long timeout); + public void saveSession(SaSession session, long timeout); /** * 更新指定session * @param session 要更新的session对象 */ - public void updateSaSession(SaSession session); + public void updateSession(SaSession session); /** - * 删除一个指定的session + * 删除一个指定的session * @param sessionId sessionId */ - public void deleteSaSession(String sessionId); + public void deleteSession(String sessionId); + + /** + * 获取指定SaSession的剩余存活时间 (单位: 秒) + * @param sessionId 指定SaSession + * @return 这个SaSession的剩余存活时间 + */ + public long getSessionTimeout(String sessionId); + + + } diff --git a/sp-token-core/src/main/java/cn/dev33/satoken/dao/SaTokenDaoDefaultImpl.java b/sp-token-core/src/main/java/cn/dev33/satoken/dao/SaTokenDaoDefaultImpl.java index a1a45cdc..51eaf6a9 100644 --- a/sp-token-core/src/main/java/cn/dev33/satoken/dao/SaTokenDaoDefaultImpl.java +++ b/sp-token-core/src/main/java/cn/dev33/satoken/dao/SaTokenDaoDefaultImpl.java @@ -17,50 +17,107 @@ public class SaTokenDaoDefaultImpl implements SaTokenDao { */ Map dataMap = new HashMap(); + /** + * 过期时间集合 (单位: 毫秒) , 记录所有key的到期时间 [注意不是剩余存活时间] + */ + Map expireMap = new HashMap(); + @Override public String getValue(String key) { + clearKeyByTimeout(key); return (String)dataMap.get(key); } @Override public void setValue(String key, String value, long timeout) { dataMap.put(key, value); + expireMap.put(key, (timeout == SaTokenDao.NEVER_EXPIRE) ? (SaTokenDao.NEVER_EXPIRE) : (System.currentTimeMillis() + timeout * 1000)); } @Override public void updateValue(String key, String value) { - this.setValue(key, value, 0); + dataMap.put(key, value); } @Override public void delKey(String key) { dataMap.remove(key); } - @Override - public SaSession getSaSession(String sessionId) { + public long getTimeout(String key) { + return getKeyTimeout(key); + } + + + @Override + public SaSession getSession(String sessionId) { + clearKeyByTimeout(sessionId); return (SaSession)dataMap.get(sessionId); } @Override - public void saveSaSession(SaSession session, long timeout) { + public void saveSession(SaSession session, long timeout) { dataMap.put(session.getId(), session); + expireMap.put(session.getId(), (timeout == SaTokenDao.NEVER_EXPIRE) ? (SaTokenDao.NEVER_EXPIRE) : (System.currentTimeMillis() + timeout * 1000)); } @Override - public void updateSaSession(SaSession session) { + public void updateSession(SaSession session) { // 无动作 } @Override - public void deleteSaSession(String sessionId) { + public void deleteSession(String sessionId) { dataMap.remove(sessionId); } + @Override + public long getSessionTimeout(String sessionId) { + return getKeyTimeout(sessionId); + } + + // --------------------- + + /** + * 如果指定key已经过期,则立即清除它 + * @param key 指定key + */ + void clearKeyByTimeout(String key) { + Long expirationTime = expireMap.get(key); + // 清除条件:如果不为空 && 不是[永不过期] && 已经超过过期时间 + if(expirationTime != null && expirationTime != SaTokenDao.NEVER_EXPIRE && expirationTime < System.currentTimeMillis()) { + dataMap.remove(key); + expireMap.remove(key); + } + } + + + /** + * 获取指定key的剩余存活时间 (单位:秒) + */ + + long getKeyTimeout(String key) { + // 先检查是否已经过期 + clearKeyByTimeout(key); + // 获取过期时间 + Long expire = expireMap.get(key); + // 如果根本没有这个值 + if(expire == null) { + return SaTokenDao.NOT_VALUE_EXPIRE; + } + // 如果被标注为永不过期 + if(expire == SaTokenDao.NEVER_EXPIRE) { + return SaTokenDao.NEVER_EXPIRE; + } + // 计算剩余时间并返回 + return (expire - System.currentTimeMillis()) / 1000; + } + + diff --git a/sp-token-core/src/main/java/cn/dev33/satoken/session/SaSession.java b/sp-token-core/src/main/java/cn/dev33/satoken/session/SaSession.java index a2871405..ba070515 100644 --- a/sp-token-core/src/main/java/cn/dev33/satoken/session/SaSession.java +++ b/sp-token-core/src/main/java/cn/dev33/satoken/session/SaSession.java @@ -137,7 +137,7 @@ public class SaSession implements Serializable { * 将这个session从持久库更新一下 */ public void update() { - SaTokenManager.getSaTokenDao().updateSaSession(this); + SaTokenManager.getSaTokenDao().updateSession(this); } diff --git a/sp-token-core/src/main/java/cn/dev33/satoken/session/SaSessionCustomUtil.java b/sp-token-core/src/main/java/cn/dev33/satoken/session/SaSessionCustomUtil.java index 0e8f38e3..24042af3 100644 --- a/sp-token-core/src/main/java/cn/dev33/satoken/session/SaSessionCustomUtil.java +++ b/sp-token-core/src/main/java/cn/dev33/satoken/session/SaSessionCustomUtil.java @@ -23,7 +23,7 @@ public class SaSessionCustomUtil { * @return 是否存在 */ public boolean isExists(String sessionId) { - return SaTokenManager.getSaTokenDao().getSaSession(getSessionKey(sessionId)) != null; + return SaTokenManager.getSaTokenDao().getSession(getSessionKey(sessionId)) != null; } /** @@ -33,13 +33,14 @@ public class SaSessionCustomUtil { * @return SaSession */ public static SaSession getSessionById(String sessionId, boolean isCreate) { - SaSession session = SaTokenManager.getSaTokenDao().getSaSession(getSessionKey(sessionId)); + SaSession session = SaTokenManager.getSaTokenDao().getSession(getSessionKey(sessionId)); if(session == null && isCreate) { session = new SaSession(getSessionKey(sessionId)); - SaTokenManager.getSaTokenDao().saveSaSession(session, SaTokenManager.getConfig().getTimeout()); + SaTokenManager.getSaTokenDao().saveSession(session, SaTokenManager.getConfig().getTimeout()); } return session; } + /** * 获取指定key的session, 如果没有则新建并返回 * @param sessionId key @@ -54,7 +55,7 @@ public class SaSessionCustomUtil { * @param sessionId 删除指定key */ public static void deleteSessionById(String sessionId) { - SaTokenManager.getSaTokenDao().deleteSaSession(getSessionKey(sessionId)); + SaTokenManager.getSaTokenDao().deleteSession(getSessionKey(sessionId)); } diff --git a/sp-token-core/src/main/java/cn/dev33/satoken/stp/StpLogic.java b/sp-token-core/src/main/java/cn/dev33/satoken/stp/StpLogic.java index 3490375c..008ef89f 100644 --- a/sp-token-core/src/main/java/cn/dev33/satoken/stp/StpLogic.java +++ b/sp-token-core/src/main/java/cn/dev33/satoken/stp/StpLogic.java @@ -108,13 +108,14 @@ public class StpLogic { } /** - * 获取当前会话的token信息:tokenName与tokenValue + * 获取当前会话的token信息:tokenName、tokenValue、timeout * @return 一个Map对象 */ - public Map getTokenInfo() { - Map map = new HashMap(); + public Map getTokenInfo() { + Map map = new HashMap(); map.put("tokenName", getTokenName()); map.put("tokenValue", getTokenValue()); + map.put("tokenTimeout", getTimeout()); return map; } @@ -137,7 +138,7 @@ public class StpLogic { if(tokenValue == null){ // 为null则创建一个新的 tokenValue = randomTokenValue(loginId); } else { - // 不为null, 并且配置不共享,则:将原来的标记为[被顶替] + // 不为null, 并且配置不共享会话,则:将原来的会话标记为[被顶替] if(config.getIsShare() == false){ // dao.delKey(getKeyTokenValue(tokenValue)); dao.updateValue(getKeyTokenValue(tokenValue), NotLoginException.BE_REPLACED); @@ -196,7 +197,7 @@ public class StpLogic { // 清除相关数据 SaTokenManager.getSaTokenDao().delKey(getKeyTokenValue(tokenValue)); // 清除token-id键值对 SaTokenManager.getSaTokenDao().delKey(getKeyLoginId(loginId)); // 清除id-token键值对 - SaTokenManager.getSaTokenDao().deleteSaSession(getKeySession(loginId)); // 清除其session + SaTokenManager.getSaTokenDao().deleteSession(getKeySession(loginId)); // 清除其session } /** @@ -214,7 +215,7 @@ public class StpLogic { // 清除相关数据 SaTokenManager.getSaTokenDao().updateValue(getKeyTokenValue(tokenValue), NotLoginException.KICK_OUT); // 标记:已被踢下线 SaTokenManager.getSaTokenDao().delKey(getKeyLoginId(loginId)); // 清除id-token键值对 - SaTokenManager.getSaTokenDao().deleteSaSession(getKeySession(loginId)); // 清除其session + SaTokenManager.getSaTokenDao().deleteSession(getKeySession(loginId)); // 清除其session } // 查询相关 @@ -366,10 +367,10 @@ public class StpLogic { * @return . */ protected SaSession getSessionBySessionId(String sessionId, boolean isCreate) { - SaSession session = SaTokenManager.getSaTokenDao().getSaSession(sessionId); + SaSession session = SaTokenManager.getSaTokenDao().getSession(sessionId); if(session == null && isCreate) { session = new SaSession(sessionId); - SaTokenManager.getSaTokenDao().saveSaSession(session, SaTokenManager.getConfig().getTimeout()); + SaTokenManager.getSaTokenDao().saveSession(session, SaTokenManager.getConfig().getTimeout()); } return session; } @@ -400,7 +401,26 @@ public class StpLogic { public SaSession getSession() { return getSessionByLoginId(getLoginId()); } + + + // =================== 过期时间相关 =================== + + /** + * 获取当前登录者的token剩余有效时间 (单位: 秒) + * @return token剩余有效时间 + */ + public long getTimeout() { + return SaTokenManager.getSaTokenDao().getTimeout(getKeyTokenValue(getTokenValue())); + } + /** + * 获取指定loginId的token剩余有效时间 (单位: 秒) + * @param loginId 指定loginId + * @return token剩余有效时间 + */ + public long getTimeoutByLoginId(Object loginId) { + return SaTokenManager.getSaTokenDao().getTimeout(getKeyTokenValue(getTokenValueByLoginId(loginId))); + } // =================== 权限验证操作 =================== diff --git a/sp-token-core/src/main/java/cn/dev33/satoken/stp/StpUtil.java b/sp-token-core/src/main/java/cn/dev33/satoken/stp/StpUtil.java index f7ac33db..3838e845 100644 --- a/sp-token-core/src/main/java/cn/dev33/satoken/stp/StpUtil.java +++ b/sp-token-core/src/main/java/cn/dev33/satoken/stp/StpUtil.java @@ -44,10 +44,10 @@ public class StpUtil { } /** - * 获取当前会话的token信息:tokenName与tokenValue + * 获取当前会话的token信息:tokenName、tokenValue、timeout * @return 一个Map对象 */ - public static Map getTokenInfo() { + public static Map getTokenInfo() { return stpLogic.getTokenInfo(); } @@ -158,6 +158,7 @@ public class StpUtil { return stpLogic.getLoginIdByToken(tokenValue); } + // =================== session相关 =================== /** @@ -187,6 +188,29 @@ public class StpUtil { return stpLogic.getSession(); } + + + // =================== 过期时间相关 =================== + + /** + * 获取当前登录者的token剩余有效时间 (单位: 秒) + * @return token剩余有效时间 + */ + public long getTimeout() { + return stpLogic.getTimeout(); + } + + /** + * 获取指定loginId的token剩余有效时间 (单位: 秒) + * @param loginId 指定loginId + * @return token剩余有效时间 + */ + public long getTimeoutByLoginId(Object loginId) { + return stpLogic.getTimeoutByLoginId(loginId); + } + + + // =================== 权限验证操作 =================== /**