v1.7.0 新特性,在Cookie模式下timeout过期时间有效

This commit is contained in:
shengzhang 2020-12-23 18:49:22 +08:00
parent 149b2e54c9
commit ded6da5554
12 changed files with 206 additions and 55 deletions

View File

@ -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.

View File

@ -36,7 +36,6 @@
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-aop/2.0.0.RELEASE/spring-boot-starter-aop-2.0.0.RELEASE.jar" enabled="true" runInBatchMode="false"/>
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-aop/5.0.4.RELEASE/spring-aop-5.0.4.RELEASE.jar" enabled="true" runInBatchMode="false"/>
<factorypathentry kind="VARJAR" id="M2_REPO/org/aspectj/aspectjweaver/1.8.13/aspectjweaver-1.8.13.jar" enabled="true" runInBatchMode="false"/>
<factorypathentry kind="VARJAR" id="M2_REPO/cn/dev33/sa-token/1.4.0/sa-token-1.4.0.jar" enabled="true" runInBatchMode="false"/>
<factorypathentry kind="VARJAR" id="M2_REPO/javax/servlet/javax.servlet-api/3.1.0/javax.servlet-api-3.1.0.jar" enabled="true" runInBatchMode="false"/>
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-redis/1.4.7.RELEASE/spring-boot-starter-redis-1.4.7.RELEASE.jar" enabled="true" runInBatchMode="false"/>
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/data/spring-data-redis/2.0.5.RELEASE/spring-data-redis-2.0.5.RELEASE.jar" enabled="true" runInBatchMode="false"/>

View File

@ -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);
}

View File

@ -57,7 +57,7 @@ public class StpUserUtil {
* 获取当前会话的token信息tokenName与tokenValue
* @return 一个Map对象
*/
public static Map<String, String> getTokenInfo() {
public static Map<String, Object> getTokenInfo() {
return stpLogic.getTokenInfo();
}

View File

@ -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) {

View File

@ -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

View File

@ -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);
}

View File

@ -17,50 +17,107 @@ public class SaTokenDaoDefaultImpl implements SaTokenDao {
*/
Map<String, Object> dataMap = new HashMap<String, Object>();
/**
* 过期时间集合 (单位: 毫秒) , 记录所有key的到期时间 [注意不是剩余存活时间]
*/
Map<String, Long> expireMap = new HashMap<String, Long>();
@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;
}

View File

@ -137,7 +137,7 @@ public class SaSession implements Serializable {
* 将这个session从持久库更新一下
*/
public void update() {
SaTokenManager.getSaTokenDao().updateSaSession(this);
SaTokenManager.getSaTokenDao().updateSession(this);
}

View File

@ -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));
}

View File

@ -108,13 +108,14 @@ public class StpLogic {
}
/**
* 获取当前会话的token信息tokenName与tokenValue
* 获取当前会话的token信息tokenNametokenValuetimeout
* @return 一个Map对象
*/
public Map<String, String> getTokenInfo() {
Map<String, String> map = new HashMap<String, String>();
public Map<String, Object> getTokenInfo() {
Map<String, Object> map = new HashMap<String, Object>();
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)));
}
// =================== 权限验证操作 ===================

View File

@ -44,10 +44,10 @@ public class StpUtil {
}
/**
* 获取当前会话的token信息tokenName与tokenValue
* 获取当前会话的token信息tokenNametokenValuetimeout
* @return 一个Map对象
*/
public static Map<String, String> getTokenInfo() {
public static Map<String, Object> 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);
}
// =================== 权限验证操作 ===================
/**