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);
+ }
+
+
+
// =================== 权限验证操作 ===================
/**