v1.7.0新特性,token临时过期时间设定

This commit is contained in:
shengzhang 2020-12-24 18:11:12 +08:00
parent ded6da5554
commit 5c684ac7f1
12 changed files with 624 additions and 116 deletions

View File

@ -10,8 +10,7 @@ public class SaTokenDemoApplication {
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(SaTokenDemoApplication.class, args); SpringApplication.run(SaTokenDemoApplication.class, args);
System.out.println("启动成功sa-token配置如下" + SaTokenManager.getConfig()); System.out.println("\n启动成功sa-token配置如下" + SaTokenManager.getConfig());
} }
} }

View File

@ -7,7 +7,6 @@ import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;
import cn.dev33.satoken.dao.SaTokenDao; import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.session.SaSession; import cn.dev33.satoken.session.SaSession;
@ -15,7 +14,7 @@ import cn.dev33.satoken.session.SaSession;
/** /**
* sa-token持久层的实现类 , 基于redis * sa-token持久层的实现类 , 基于redis
*/ */
@Component // 打开此注解保证此类被springboot扫描即可完成sa-token与redis的集成 //@Component // 打开此注解保证此类被springboot扫描即可完成sa-token与redis的集成
public class SaTokenDaoRedis implements SaTokenDao { public class SaTokenDaoRedis implements SaTokenDao {
@ -58,12 +57,13 @@ public class SaTokenDaoRedis implements SaTokenDao {
if(expire == SaTokenDao.NOT_VALUE_EXPIRE) { // -2 = 无此键 if(expire == SaTokenDao.NOT_VALUE_EXPIRE) { // -2 = 无此键
return; return;
} }
stringRedisTemplate.opsForValue().set(key, value, expire, TimeUnit.SECONDS); // stringRedisTemplate.opsForValue().set(key, value, expire, TimeUnit.SECONDS);
this.setValue(key, value, expire);
} }
// 删除一个指定的key // 删除一个指定的key
@Override @Override
public void delKey(String key) { public void deleteKey(String key) {
stringRedisTemplate.delete(key); stringRedisTemplate.delete(key);
} }
@ -99,7 +99,8 @@ public class SaTokenDaoRedis implements SaTokenDao {
if(expire == SaTokenDao.NOT_VALUE_EXPIRE) { // -2 = 无此键 if(expire == SaTokenDao.NOT_VALUE_EXPIRE) { // -2 = 无此键
return; return;
} }
redisTemplate.opsForValue().set(session.getId(), session, expire, TimeUnit.SECONDS); // redisTemplate.opsForValue().set(session.getId(), session, expire, TimeUnit.SECONDS);
this.saveSession(session, expire);
} }
// 删除一个指定的session // 删除一个指定的session
@ -108,11 +109,7 @@ public class SaTokenDaoRedis implements SaTokenDao {
redisTemplate.delete(sessionId); redisTemplate.delete(sessionId);
} }
// 获取指定SaSession的剩余存活时间 (单位: ) // 获取指定SaSession的剩余存活时间 (单位: )
@Override @Override
public long getSessionTimeout(String sessionId) { public long getSessionTimeout(String sessionId) {
return redisTemplate.getExpire(sessionId); return redisTemplate.getExpire(sessionId);

View File

@ -1,29 +1,19 @@
package com.pj.satoken; package com.pj.satoken;
import java.util.Map;
import org.springframework.stereotype.Service;
import cn.dev33.satoken.SaTokenManager;
import cn.dev33.satoken.session.SaSession; import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpLogic; import cn.dev33.satoken.stp.StpLogic;
/** /**
* 一个默认的实现 * 一个默认的实现
* @author kong * @author kong
*/ */
@Service
public class StpUserUtil { public class StpUserUtil {
/** /**
* 底层的 StpLogic 对象 * 底层的 StpLogic 对象
*/ */
public static StpLogic stpLogic = new StpLogic("user") { public static StpLogic stpLogic = new StpLogic("user");
@Override
public String getKeyTokenName() {
return SaTokenManager.getConfig().getTokenName() + "-user";
}
};
// =================== 获取token 相关 =================== // =================== 获取token 相关 ===================
@ -54,10 +44,18 @@ public class StpUserUtil {
} }
/** /**
* 获取当前会话的token信息tokenName与tokenValue * 获取当前StpLogin的loginKey
* @return 一个Map对象 * @return 当前StpLogin的loginKey
*/ */
public static Map<String, Object> getTokenInfo() { public static String getLoginKey(){
return stpLogic.getLoginKey();
}
/**
* 获取当前会话的token信息
* @return token信息
*/
public static SaTokenInfo getTokenInfo() {
return stpLogic.getTokenInfo(); return stpLogic.getTokenInfo();
} }
@ -79,13 +77,21 @@ public class StpUserUtil {
} }
/** /**
* 指定loginId的会话注销登录踢人下线 * 指定loginId的会话注销登录清退下线
* @param loginId 账号id * @param loginId 账号id
*/ */
public static void logoutByLoginId(Object loginId) { public static void logoutByLoginId(Object loginId) {
stpLogic.logoutByLoginId(loginId); stpLogic.logoutByLoginId(loginId);
} }
/**
* 指定loginId的会话注销登录踢人下线
* @param loginId 账号id
*/
public static void kickoutByLoginId(Object loginId) {
stpLogic.kickoutByLoginId(loginId);
}
// 查询相关 // 查询相关
/** /**
@ -160,6 +166,7 @@ public class StpUserUtil {
return stpLogic.getLoginIdByToken(tokenValue); return stpLogic.getLoginIdByToken(tokenValue);
} }
// =================== session相关 =================== // =================== session相关 ===================
/** /**
@ -189,6 +196,46 @@ public class StpUserUtil {
return stpLogic.getSession(); return stpLogic.getSession();
} }
// =================== 过期时间相关 ===================
/**
* 获取当前登录者的token剩余有效时间 (单位: )
* @return token剩余有效时间
*/
public long getTimeout() {
return stpLogic.getTokenTimeout();
}
/**
* 获取指定loginId的token剩余有效时间 (单位: )
* @param loginId 指定loginId
* @return token剩余有效时间
*/
public long getTimeoutByLoginId(Object loginId) {
return stpLogic.getTokenTimeoutByLoginId(loginId);
}
/**
* 获取当前登录者的Session剩余有效时间 (单位: )
* @return token剩余有效时间
*/
public long getSessionTimeout() {
return stpLogic.getSessionTimeout();
}
/**
* 获取指定loginId的Session剩余有效时间 (单位: )
* @param loginId 指定loginId
* @return token剩余有效时间
*/
public long getSessionTimeoutByLoginId(Object loginId) {
return stpLogic.getSessionTimeoutByLoginId(loginId);
}
// =================== 权限验证操作 =================== // =================== 权限验证操作 ===================
/** /**

View File

@ -7,6 +7,7 @@ import org.springframework.web.bind.annotation.RestController;
import cn.dev33.satoken.annotation.SaCheckLogin; import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.session.SaSessionCustomUtil; import cn.dev33.satoken.session.SaSessionCustomUtil;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
/** /**
@ -19,15 +20,6 @@ import cn.dev33.satoken.stp.StpUtil;
public class TestController { 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 // 测试登录接口 浏览器访问 http://localhost:8081/test/login
@RequestMapping("login") @RequestMapping("login")
public AjaxJson login(@RequestParam(defaultValue="10001") String id) { public AjaxJson login(@RequestParam(defaultValue="10001") String id) {
@ -107,8 +99,9 @@ public class TestController {
@RequestMapping("tokenInfo") @RequestMapping("tokenInfo")
public AjaxJson tokenInfo() { public AjaxJson tokenInfo() {
System.out.println("======================= 进入方法打印当前token信息 ========================= "); System.out.println("======================= 进入方法打印当前token信息 ========================= ");
System.out.println(StpUtil.getTokenInfo()); SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
return AjaxJson.getSuccess(); System.out.println(tokenInfo);
return AjaxJson.getSuccessData(tokenInfo);
} }
@ -122,14 +115,21 @@ public class TestController {
return AjaxJson.getSuccess(); return AjaxJson.getSuccess();
} }
// 测试注解式鉴权 浏览器访问 http://localhost:8081/test/getInfo // 测试注解式鉴权 浏览器访问 http://localhost:8081/test/atLogin
@SaCheckLogin // 注解式鉴权当前会话必须登录才能通过 @SaCheckLogin // 注解式鉴权当前会话必须登录才能通过
@RequestMapping("getInfo") @RequestMapping("atLogin")
public AjaxJson getInfo() { public AjaxJson atLogin() {
return AjaxJson.getSuccessData("用户信息"); return AjaxJson.getSuccessData("用户信息");
} }
// [活动时间] 续签 http://localhost:8081/test/rene
@RequestMapping("rene")
public AjaxJson rene() {
StpUtil.checkActivityTimeout();
StpUtil.updateLastActivityToNow();
return AjaxJson.getSuccess("续签成功");
}
// 测试踢人下线 浏览器访问 http://localhost:8081/test/kickOut // 测试踢人下线 浏览器访问 http://localhost:8081/test/kickOut

View File

@ -8,8 +8,9 @@ spring:
# token名称 (同时也是cookie名称) # token名称 (同时也是cookie名称)
token-name: satoken token-name: satoken
# token有效期单位s 默认30天, -1代表永不过期 # token有效期单位s 默认30天, -1代表永不过期
# timeout: 2592000 timeout: 2592000
timeout: -1 # token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒, 默认-1 代表不限制 (例如可以设置为1800代表30分钟内无操作就过期)
activity-timeout: -1
# 在多人登录同一账号时,是否共享会话 (为true时共用一个为false时新登录挤掉旧登录) # 在多人登录同一账号时,是否共享会话 (为true时共用一个为false时新登录挤掉旧登录)
is-share: true is-share: true
# 是否尝试从请求体里读取token # 是否尝试从请求体里读取token

View File

@ -9,6 +9,7 @@ public class SaTokenConfig {
private String tokenName = "satoken"; // token名称 (同时也是cookie名称) private String tokenName = "satoken"; // token名称 (同时也是cookie名称)
private long timeout = 30 * 24 * 60 * 60; // token有效期单位s 默认30天 private long timeout = 30 * 24 * 60 * 60; // token有效期单位s 默认30天
private long activityTimeout = -1; // token临时有效期 (指定时间内无操作就视为token过期) 单位: , 默认-1 代表不限制 (例如可以设置为1800代表30分钟内无操作就过期)
private Boolean isShare = true; // 在多人登录同一账号时是否共享会话 (为true时共用一个为false时新登录挤掉旧登录) private Boolean isShare = true; // 在多人登录同一账号时是否共享会话 (为true时共用一个为false时新登录挤掉旧登录)
private Boolean isReadBody = true; // 是否尝试从请求体里读取token private Boolean isReadBody = true; // 是否尝试从请求体里读取token
private Boolean isReadHead = true; // 是否尝试从header里读取token private Boolean isReadHead = true; // 是否尝试从header里读取token
@ -48,6 +49,20 @@ public class SaTokenConfig {
this.timeout = timeout; this.timeout = timeout;
} }
/**
* @return activityTimeout
*/
public long getActivityTimeout() {
return activityTimeout;
}
/**
* @param activityTimeout 要设置的 activityTimeout
*/
public void setActivityTimeout(long activityTimeout) {
this.activityTimeout = activityTimeout;
}
/** /**
* @return isShare * @return isShare
*/ */
@ -137,9 +152,9 @@ public class SaTokenConfig {
@Override @Override
public String toString() { public String toString() {
return "SaTokenConfig [tokenName=" + tokenName + ", timeout=" + timeout + ", isShare=" + isShare return "SaTokenConfig [tokenName=" + tokenName + ", timeout=" + timeout + ", activityTimeout=" + activityTimeout
+ ", isReadBody=" + isReadBody + ", isReadHead=" + isReadHead + ", isReadCookie=" + isReadCookie + ", isShare=" + isShare + ", isReadBody=" + isReadBody + ", isReadHead=" + isReadHead
+ ", tokenStyle=" + tokenStyle + ", isV=" + isV + "]"; + ", isReadCookie=" + isReadCookie + ", tokenStyle=" + tokenStyle + ", isV=" + isV + "]";
} }
@ -154,4 +169,8 @@ public class SaTokenConfig {
} }

View File

@ -44,7 +44,7 @@ public interface SaTokenDao {
* 删除一个指定的key * 删除一个指定的key
* @param key 键名称 * @param key 键名称
*/ */
public void delKey(String key); public void deleteKey(String key);
/** /**
* 获取指定key的剩余存活时间 (单位: ) * 获取指定key的剩余存活时间 (单位: )

View File

@ -15,12 +15,12 @@ public class SaTokenDaoDefaultImpl implements SaTokenDao {
/** /**
* 所有数据集合 * 所有数据集合
*/ */
Map<String, Object> dataMap = new HashMap<String, Object>(); public Map<String, Object> dataMap = new HashMap<String, Object>();
/** /**
* 过期时间集合 (单位: 毫秒) , 记录所有key的到期时间 [注意不是剩余存活时间] * 过期时间集合 (单位: 毫秒) , 记录所有key的到期时间 [注意不是剩余存活时间]
*/ */
Map<String, Long> expireMap = new HashMap<String, Long>(); public Map<String, Long> expireMap = new HashMap<String, Long>();
@Override @Override
@ -37,12 +37,16 @@ public class SaTokenDaoDefaultImpl implements SaTokenDao {
@Override @Override
public void updateValue(String key, String value) { public void updateValue(String key, String value) {
if(getKeyTimeout(key) == SaTokenDao.NOT_VALUE_EXPIRE) {
return;
}
dataMap.put(key, value); dataMap.put(key, value);
} }
@Override @Override
public void delKey(String key) { public void deleteKey(String key) {
dataMap.remove(key); dataMap.remove(key);
expireMap.remove(key);
} }
@Override @Override
@ -65,12 +69,16 @@ public class SaTokenDaoDefaultImpl implements SaTokenDao {
@Override @Override
public void updateSession(SaSession session) { public void updateSession(SaSession session) {
if(getKeyTimeout(session.getId()) == SaTokenDao.NOT_VALUE_EXPIRE) {
return;
}
// 无动作 // 无动作
} }
@Override @Override
public void deleteSession(String sessionId) { public void deleteSession(String sessionId) {
dataMap.remove(sessionId); dataMap.remove(sessionId);
expireMap.remove(sessionId);
} }
@Override @Override
@ -99,7 +107,6 @@ public class SaTokenDaoDefaultImpl implements SaTokenDao {
/** /**
* 获取指定key的剩余存活时间 (单位) * 获取指定key的剩余存活时间 (单位)
*/ */
long getKeyTimeout(String key) { long getKeyTimeout(String key) {
// 先检查是否已经过期 // 先检查是否已经过期
clearKeyByTimeout(key); clearKeyByTimeout(key);
@ -113,8 +120,15 @@ public class SaTokenDaoDefaultImpl implements SaTokenDao {
if(expire == SaTokenDao.NEVER_EXPIRE) { if(expire == SaTokenDao.NEVER_EXPIRE) {
return SaTokenDao.NEVER_EXPIRE; return SaTokenDao.NEVER_EXPIRE;
} }
// 计算剩余时间并返回 // ---- 计算剩余时间并返回
return (expire - System.currentTimeMillis()) / 1000; long timeout = (expire - System.currentTimeMillis()) / 1000;
// 小于零时视为不存在
if(timeout < 0) {
dataMap.remove(key);
expireMap.remove(key);
return SaTokenDao.NOT_VALUE_EXPIRE;
}
return timeout;
} }

View File

@ -0,0 +1,179 @@
package cn.dev33.satoken.stp;
/**
* 用来描述一个token常用信息的类
* @author kong
*
*/
public class SaTokenInfo {
/** token名称 */
public String tokenName;
/** token值 */
public String tokenValue;
/** 当前是否已经登录 */
public Boolean isLogin;
/** 当前loginId未登录时为null */
public Object loginId;
/** 当前loginKey */
public String loginKey;
/** token剩余有效期 (单位: 秒) */
public long tokenTimeout;
/** session剩余有效时间 (单位: 秒) */
public long sessionTimeout;
/**
* token剩余无操作有效时间
*/
public long tokenActivityTimeout;
/**
* @return tokenName
*/
public String getTokenName() {
return tokenName;
}
/**
* @param tokenName 要设置的 tokenName
*/
public SaTokenInfo setTokenName(String tokenName) {
this.tokenName = tokenName;
return this;
}
/**
* @return tokenValue
*/
public String getTokenValue() {
return tokenValue;
}
/**
* @param tokenValue 要设置的 tokenValue
*/
public SaTokenInfo setTokenValue(String tokenValue) {
this.tokenValue = tokenValue;
return this;
}
/**
* @return isLogin
*/
public Boolean getIsLogin() {
return isLogin;
}
/**
* @param isLogin 要设置的 isLogin
*/
public SaTokenInfo setIsLogin(Boolean isLogin) {
this.isLogin = isLogin;
return this;
}
/**
* @return loginId
*/
public Object getLoginId() {
return loginId;
}
/**
* @param loginId 要设置的 loginId
*/
public SaTokenInfo setLoginId(Object loginId) {
this.loginId = loginId;
return this;
}
/**
* @return loginKey
*/
public String getLoginKey() {
return loginKey;
}
/**
* @param loginKey 要设置的 loginKey
*/
public SaTokenInfo setLoginKey(String loginKey) {
this.loginKey = loginKey;
return this;
}
/**
* @return tokenTimeout
*/
public long getTokenTimeout() {
return tokenTimeout;
}
/**
* @param tokenTimeout 要设置的 tokenTimeout
*/
public SaTokenInfo setTokenTimeout(long tokenTimeout) {
this.tokenTimeout = tokenTimeout;
return this;
}
/**
* @return sessionTimeout
*/
public long getSessionTimeout() {
return sessionTimeout;
}
/**
* @param sessionTimeout 要设置的 sessionTimeout
*/
public SaTokenInfo setSessionTimeout(long sessionTimeout) {
this.sessionTimeout = sessionTimeout;
return this;
}
/**
* @return tokenActivityTimeout
*/
public long getTokenActivityTimeout() {
return tokenActivityTimeout;
}
/**
* @param tokenActivityTimeout 要设置的 tokenActivityTimeout
*/
public SaTokenInfo setTokenActivityTimeout(long tokenActivityTimeout) {
this.tokenActivityTimeout = tokenActivityTimeout;
return this;
}
@Override
public String toString() {
return "SaTokenInfo [tokenName=" + tokenName + ", tokenValue=" + tokenValue + ", isLogin=" + isLogin
+ ", loginId=" + loginId + ", loginKey=" + loginKey + ", tokenTimeout=" + tokenTimeout
+ ", sessionTimeout=" + sessionTimeout + ", tokenActivityTimeout=" + tokenActivityTimeout + "]";
}
}

View File

@ -1,8 +1,6 @@
package cn.dev33.satoken.stp; package cn.dev33.satoken.stp;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import javax.servlet.http.Cookie; import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -24,7 +22,7 @@ import cn.dev33.satoken.util.SaTokenInsideUtil;
public class StpLogic { public class StpLogic {
/** /**
* 持久化的key前缀多账号体系时以此值区分比如loginuseradmin * 持久化的key前缀多账号认证体系时以此值区分比如loginuseradmin
*/ */
public String loginKey = ""; public String loginKey = "";
@ -61,42 +59,35 @@ public class StpLogic {
* @return 当前tokenValue * @return 当前tokenValue
*/ */
public String getTokenValue(){ public String getTokenValue(){
// 0获取相应对象 // 0. 获取相应对象
HttpServletRequest request = SaTokenManager.getSaTokenServlet().getRequest(); HttpServletRequest request = SaTokenManager.getSaTokenServlet().getRequest();
SaTokenConfig config = SaTokenManager.getConfig(); SaTokenConfig config = SaTokenManager.getConfig();
String keyTokenName = getTokenName(); String keyTokenName = getTokenName();
String tokenValue = null;
// 1尝试从request里读取 // 1. 尝试从request里读取
if(request.getAttribute(SaTokenInsideUtil.JUST_CREATED_SAVE_KEY) != null) { if(request.getAttribute(SaTokenInsideUtil.JUST_CREATED_SAVE_KEY) != null) {
return String.valueOf(request.getAttribute(SaTokenInsideUtil.JUST_CREATED_SAVE_KEY)); tokenValue = String.valueOf(request.getAttribute(SaTokenInsideUtil.JUST_CREATED_SAVE_KEY));
} }
// 2尝试从请求体里面读取 // 2. 尝试从请求体里面读取
if(config.getIsReadBody() == true){ if(tokenValue == null && config.getIsReadBody() == true){
String tokenValue = request.getParameter(keyTokenName); tokenValue = request.getParameter(keyTokenName);
if(tokenValue != null) {
return tokenValue;
} }
// 3. 尝试从header力读取
if(tokenValue == null && config.getIsReadHead() == true){
tokenValue = request.getHeader(keyTokenName);
} }
// 3尝试从header力读取 // 4. 尝试从cookie里读取
if(config.getIsReadHead() == true){ if(tokenValue == null && config.getIsReadCookie() == true){
String tokenValue = request.getHeader(keyTokenName);
if(tokenValue != null) {
return tokenValue;
}
}
// 4尝试从cookie里读取
if(config.getIsReadCookie() == true){
Cookie cookie = SaTokenManager.getSaTokenCookie().getCookie(request, keyTokenName); Cookie cookie = SaTokenManager.getSaTokenCookie().getCookie(request, keyTokenName);
if(cookie != null){ if(cookie != null){
String tokenValue = cookie.getValue(); tokenValue = cookie.getValue();
if(tokenValue != null) { }
}
// 5. 返回
return tokenValue; return tokenValue;
} }
}
}
// 5都读取不到那算了吧还是
return null;
}
/** /**
* 获取指定id的tokenValue * 获取指定id的tokenValue
@ -108,15 +99,28 @@ public class StpLogic {
} }
/** /**
* 获取当前会话的token信息tokenNametokenValuetimeout * 获取当前StpLogin的loginKey
* @return 一个Map对象 * @return 当前StpLogin的loginKey
*/ */
public Map<String, Object> getTokenInfo() { public String getLoginKey(){
Map<String, Object> map = new HashMap<String, Object>(); return loginKey;
map.put("tokenName", getTokenName()); }
map.put("tokenValue", getTokenValue());
map.put("tokenTimeout", getTimeout()); /**
return map; * 获取当前会话的token信息
* @return token信息
*/
public SaTokenInfo getTokenInfo() {
SaTokenInfo info = new SaTokenInfo();
info.tokenName = getTokenName();
info.tokenValue = getTokenValue();
info.isLogin = isLogin();
info.loginId = getLoginIdDefaultNull();
info.loginKey = getLoginKey();
info.tokenTimeout = getTokenTimeout();
info.sessionTimeout = getSessionTimeout();
info.tokenActivityTimeout = getTokenActivityTimeout();
return info;
} }
@ -140,9 +144,9 @@ public class StpLogic {
} else { } else {
// 不为null, 并且配置不共享会话将原来的会话标记为[被顶替] // 不为null, 并且配置不共享会话将原来的会话标记为[被顶替]
if(config.getIsShare() == false){ if(config.getIsShare() == false){
// dao.delKey(getKeyTokenValue(tokenValue));
dao.updateValue(getKeyTokenValue(tokenValue), NotLoginException.BE_REPLACED); dao.updateValue(getKeyTokenValue(tokenValue), NotLoginException.BE_REPLACED);
tokenValue = randomTokenValue(loginId); clearLastActivity(tokenValue); // 同时清理掉[最后操作时间]
tokenValue = randomTokenValue(loginId); // 再重新生成一个token
} }
} }
@ -150,6 +154,7 @@ public class StpLogic {
dao.setValue(getKeyTokenValue(tokenValue), String.valueOf(loginId), config.getTimeout()); // token -> uid dao.setValue(getKeyTokenValue(tokenValue), String.valueOf(loginId), config.getTimeout()); // token -> uid
dao.setValue(getKeyLoginId(loginId), tokenValue, config.getTimeout()); // uid -> token dao.setValue(getKeyLoginId(loginId), tokenValue, config.getTimeout()); // uid -> token
request.setAttribute(SaTokenInsideUtil.JUST_CREATED_SAVE_KEY, tokenValue); // 保存到本次request里 request.setAttribute(SaTokenInsideUtil.JUST_CREATED_SAVE_KEY, tokenValue); // 保存到本次request里
setLastActivityToNow(tokenValue); // 写入 [最后操作时间]
if(config.getIsReadCookie() == true){ if(config.getIsReadCookie() == true){
SaTokenManager.getSaTokenCookie().addCookie(SaTokenManager.getSaTokenServlet().getResponse(), getTokenName(), tokenValue, "/", (int)config.getTimeout()); // cookie注入 SaTokenManager.getSaTokenCookie().addCookie(SaTokenManager.getSaTokenServlet().getResponse(), getTokenName(), tokenValue, "/", (int)config.getTimeout()); // cookie注入
} }
@ -183,7 +188,7 @@ public class StpLogic {
} }
/** /**
* 指定loginId的会话注销登录清退下线 * 指定loginId的会话注销登录正常注销下线
* @param loginId 账号id * @param loginId 账号id
*/ */
public void logoutByLoginId(Object loginId) { public void logoutByLoginId(Object loginId) {
@ -195,9 +200,10 @@ public class StpLogic {
} }
// 清除相关数据 // 清除相关数据
SaTokenManager.getSaTokenDao().delKey(getKeyTokenValue(tokenValue)); // 清除token-id键值对 SaTokenManager.getSaTokenDao().deleteKey(getKeyTokenValue(tokenValue)); // 清除token-id键值对
SaTokenManager.getSaTokenDao().delKey(getKeyLoginId(loginId)); // 清除id-token键值对 SaTokenManager.getSaTokenDao().deleteKey(getKeyLoginId(loginId)); // 清除id-token键值对
SaTokenManager.getSaTokenDao().deleteSession(getKeySession(loginId)); // 清除其session SaTokenManager.getSaTokenDao().deleteSession(getKeySession(loginId)); // 清除其session
clearLastActivity(tokenValue); // 同时清理掉 [最后操作时间]
} }
/** /**
@ -214,8 +220,9 @@ public class StpLogic {
// 清除相关数据 // 清除相关数据
SaTokenManager.getSaTokenDao().updateValue(getKeyTokenValue(tokenValue), NotLoginException.KICK_OUT); // 标记已被踢下线 SaTokenManager.getSaTokenDao().updateValue(getKeyTokenValue(tokenValue), NotLoginException.KICK_OUT); // 标记已被踢下线
SaTokenManager.getSaTokenDao().delKey(getKeyLoginId(loginId)); // 清除id-token键值对 SaTokenManager.getSaTokenDao().deleteKey(getKeyLoginId(loginId)); // 清除id-token键值对
SaTokenManager.getSaTokenDao().deleteSession(getKeySession(loginId)); // 清除其session SaTokenManager.getSaTokenDao().deleteSession(getKeySession(loginId)); // 清除其session
clearLastActivity(tokenValue); // 同时清理掉 [最后操作时间]
} }
// 查询相关 // 查询相关
@ -263,6 +270,9 @@ public class StpLogic {
if(loginId.equals(NotLoginException.KICK_OUT)) { if(loginId.equals(NotLoginException.KICK_OUT)) {
throw NotLoginException.newInstance(loginKey, NotLoginException.KICK_OUT); throw NotLoginException.newInstance(loginKey, NotLoginException.KICK_OUT);
} }
// 检查是否已经 [临时过期]同时更新[最后操作时间]
checkActivityTimeout(tokenValue);
updateLastActivityToNow(tokenValue);
// 至此返回loginId // 至此返回loginId
return loginId; return loginId;
} }
@ -307,6 +317,10 @@ public class StpLogic {
if(loginId == null || NotLoginException.ABNORMAL_LIST.contains(loginId)) { if(loginId == null || NotLoginException.ABNORMAL_LIST.contains(loginId)) {
return null; return null;
} }
// 如果已经[临时过期]
if(getTokenActivityTimeoutByToken(tokenValue) == SaTokenDao.NOT_VALUE_EXPIRE) {
return null;
}
// 执行到此证明loginId已经是个正常的账号id了 // 执行到此证明loginId已经是个正常的账号id了
return loginId; return loginId;
} }
@ -361,7 +375,7 @@ public class StpLogic {
// =================== session相关 =================== // =================== session相关 ===================
/** /**
* 获取指定key的session, 如果没有isCreate=是否新建并返回 * 获取指定key的session, 如果session尚未创建isCreate=是否新建并返回
* @param sessionId . * @param sessionId .
* @param isCreate . * @param isCreate .
* @return . * @return .
@ -394,22 +408,122 @@ public class StpLogic {
return getSessionByLoginId(loginId, true); return getSessionByLoginId(loginId, true);
} }
/**
* 获取当前会话的session, 如果session尚未创建isCreate=是否新建并返回
* @param isCreate 是否新建
* @return 当前会话的session
*/
public SaSession getSession(boolean isCreate) {
return getSessionByLoginId(getLoginId(), isCreate);
}
/** /**
* 获取当前会话的session * 获取当前会话的session
* @return * @return 当前会话的session
*/ */
public SaSession getSession() { public SaSession getSession() {
return getSessionByLoginId(getLoginId()); return getSession(true);
} }
// =================== [临时过期] 验证相关 ===================
/**
* 写入指定token的 [最后操作时间] 为当前时间戳
* @param tokenValue 指定token
*/
protected void setLastActivityToNow(String tokenValue) {
// 如果token == null 或者 设置了[永不过期], 则立即返回
if(tokenValue == null || SaTokenManager.getConfig().getActivityTimeout() == SaTokenDao.NEVER_EXPIRE) {
return;
}
// [最后操作时间]标记为当前时间戳
SaTokenManager.getSaTokenDao().setValue(getKeyLastActivityTime(tokenValue), String.valueOf(System.currentTimeMillis()), SaTokenManager.getConfig().getTimeout());
}
/**
* 清除指定token的 [最后操作时间]
* @param tokenValue 指定token
*/
protected void clearLastActivity(String tokenValue) {
// 如果token == null 或者 设置了[永不过期], 则立即返回
if(tokenValue == null || SaTokenManager.getConfig().getActivityTimeout() == SaTokenDao.NEVER_EXPIRE) {
return;
}
// 删除[最后操作时间]
SaTokenManager.getSaTokenDao().deleteKey(getKeyLastActivityTime(tokenValue));
// 清除标记
SaTokenManager.getSaTokenServlet().getRequest().removeAttribute(SaTokenInsideUtil.TOKEN_ACTIVITY_TIMEOUT_CHECKED_KEY);
}
/**
* 检查指定token 是否已经[临时过期]如果已经过期则抛出异常
* @param tokenValue 指定token
*/
public void checkActivityTimeout(String tokenValue) {
// 如果token == null 或者 设置了[永不过期], 则立即返回
if(tokenValue == null || SaTokenManager.getConfig().getActivityTimeout() == SaTokenDao.NEVER_EXPIRE) {
return;
}
// 如果本次请求已经有了[检查标记], 则立即返回
HttpServletRequest request = SaTokenManager.getSaTokenServlet().getRequest();
if(request.getAttribute(SaTokenInsideUtil.TOKEN_ACTIVITY_TIMEOUT_CHECKED_KEY) != null) {
return;
}
// ------------ 验证是否已经 [临时过期]
// 获取 [临时剩余时间]
long timeout = getTokenActivityTimeoutByToken(tokenValue);
// -1 代表此token已经被设置永不过期无须继续验证
if(timeout == SaTokenDao.NEVER_EXPIRE) {
return;
}
// -2 代表已过期抛出异常
if(timeout == SaTokenDao.NOT_VALUE_EXPIRE) {
throw NotLoginException.newInstance(loginKey, NotLoginException.TOKEN_TIMEOUT);
}
// --- 至此验证已通过
// 打上[检查标记]标记一下当前请求已经通过验证避免一次请求多次验证造成不必要的性能消耗
request.setAttribute(SaTokenInsideUtil.TOKEN_ACTIVITY_TIMEOUT_CHECKED_KEY, true);
}
/**
* 检查当前token 是否已经[临时过期]如果已经过期则抛出异常
*/
public void checkActivityTimeout() {
checkActivityTimeout(getTokenValue());
}
/**
* 续签指定token( [最后操作时间] 更新为当前时间戳)
* @param tokenValue 指定token
*/
public void updateLastActivityToNow(String tokenValue) {
// 如果token == null 或者 设置了[永不过期], 则立即返回
if(tokenValue == null || SaTokenManager.getConfig().getActivityTimeout() == SaTokenDao.NEVER_EXPIRE) {
return;
}
SaTokenManager.getSaTokenDao().updateValue(getKeyLastActivityTime(tokenValue), String.valueOf(System.currentTimeMillis()));
}
/**
* 续签当前token( [最后操作时间] 更新为当前时间戳)
* <h1>请注意: 即时token已经 [临时过期] 也可续签成功
* 如果此场景下需要提示续签失败可在此之前调用 checkActivityTimeout() 强制检查是否过期即可 </h1>
*/
public void updateLastActivityToNow() {
updateLastActivityToNow(getTokenValue());
}
// =================== 过期时间相关 =================== // =================== 过期时间相关 ===================
/** /**
* 获取当前登录者的token剩余有效时间 (单位: ) * 获取当前登录者的token剩余有效时间 (单位: )
* @return token剩余有效时间 * @return token剩余有效时间
*/ */
public long getTimeout() { public long getTokenTimeout() {
return SaTokenManager.getSaTokenDao().getTimeout(getKeyTokenValue(getTokenValue())); return SaTokenManager.getSaTokenDao().getTimeout(getKeyTokenValue(getTokenValue()));
} }
@ -418,10 +532,68 @@ public class StpLogic {
* @param loginId 指定loginId * @param loginId 指定loginId
* @return token剩余有效时间 * @return token剩余有效时间
*/ */
public long getTimeoutByLoginId(Object loginId) { public long getTokenTimeoutByLoginId(Object loginId) {
return SaTokenManager.getSaTokenDao().getTimeout(getKeyTokenValue(getTokenValueByLoginId(loginId))); return SaTokenManager.getSaTokenDao().getTimeout(getKeyTokenValue(getTokenValueByLoginId(loginId)));
} }
/**
* 获取当前登录者的Session剩余有效时间 (单位: )
* @return token剩余有效时间
*/
public long getSessionTimeout() {
return getSessionTimeoutByLoginId(getLoginIdDefaultNull());
}
/**
* 获取指定loginId的Session剩余有效时间 (单位: )
* @param loginId 指定loginId
* @return token剩余有效时间
*/
public long getSessionTimeoutByLoginId(Object loginId) {
return SaTokenManager.getSaTokenDao().getSessionTimeout(getKeySession(loginId));
}
/**
* 获取当前token[临时过期]剩余有效时间 (单位: )
* @return token[临时过期]剩余有效时间
*/
public long getTokenActivityTimeout() {
return getTokenActivityTimeoutByToken(getTokenValue());
}
/**
* 获取指定token[临时过期]剩余有效时间 (单位: )
* @param tokenValue 指定token
* @return token[临时过期]剩余有效时间
*/
public long getTokenActivityTimeoutByToken(String tokenValue) {
// 如果token为null , 则返回 -2
if(tokenValue == null) {
return SaTokenDao.NOT_VALUE_EXPIRE;
}
// 如果设置了永不过期, 则返回 -1
if(SaTokenManager.getConfig().getActivityTimeout() == SaTokenDao.NEVER_EXPIRE) {
return SaTokenDao.NEVER_EXPIRE;
}
// ------ 开始查询
// 获取相关数据
String keyLastActivityTime = getKeyLastActivityTime(tokenValue);
String lastActivityTimeString = SaTokenManager.getSaTokenDao().getValue(keyLastActivityTime);
// 查不到返回-2
if(lastActivityTimeString == null) {
return SaTokenDao.NOT_VALUE_EXPIRE;
}
// 计算相差时间
long lastActivityTime = Long.valueOf(lastActivityTimeString);
long apartSecond = (System.currentTimeMillis() - lastActivityTime) / 1000;
long timeout = SaTokenManager.getConfig().getActivityTimeout() - apartSecond;
// 如果 < 0 代表已经过期 返回-2
if(timeout < 0) {
return SaTokenDao.NOT_VALUE_EXPIRE;
}
return timeout;
}
// =================== 权限验证操作 =================== // =================== 权限验证操作 ===================
@ -520,6 +692,14 @@ public class StpLogic {
public String getKeySession(Object loginId) { public String getKeySession(Object loginId) {
return SaTokenManager.getConfig().getTokenName() + ":" + loginKey + ":session:" + loginId; return SaTokenManager.getConfig().getTokenName() + ":" + loginKey + ":session:" + loginId;
} }
/**
* 获取key 指定token的最后操作时间 持久化
* @param loginId .
* @return .
*/
public String getKeyLastActivityTime(String tokenValue) {
return SaTokenManager.getConfig().getTokenName() + ":" + loginKey + ":last-activity:" + tokenValue;
}
} }

View File

@ -1,7 +1,5 @@
package cn.dev33.satoken.stp; package cn.dev33.satoken.stp;
import java.util.Map;
import cn.dev33.satoken.session.SaSession; import cn.dev33.satoken.session.SaSession;
/** /**
@ -44,10 +42,18 @@ public class StpUtil {
} }
/** /**
* 获取当前会话的token信息tokenNametokenValuetimeout * 获取当前StpLogin的loginKey
* @return 一个Map对象 * @return 当前StpLogin的loginKey
*/ */
public static Map<String, Object> getTokenInfo() { public static String getLoginKey(){
return stpLogic.getLoginKey();
}
/**
* 获取当前会话的token信息
* @return token信息
*/
public static SaTokenInfo getTokenInfo() {
return stpLogic.getTokenInfo(); return stpLogic.getTokenInfo();
} }
@ -69,7 +75,7 @@ public class StpUtil {
} }
/** /**
* 指定loginId的会话注销登录清退下线 * 指定loginId的会话注销登录正常注销下线
* @param loginId 账号id * @param loginId 账号id
*/ */
public static void logoutByLoginId(Object loginId) { public static void logoutByLoginId(Object loginId) {
@ -180,6 +186,15 @@ public class StpUtil {
return stpLogic.getSessionByLoginId(loginId); return stpLogic.getSessionByLoginId(loginId);
} }
/**
* 获取当前会话的session, 如果没有isCreate=是否新建并返回
* @param isCreate 是否新建
* @return 当前会话的session
*/
public static SaSession getSession(boolean isCreate) {
return stpLogic.getSession(isCreate);
}
/** /**
* 获取当前会话的session * 获取当前会话的session
* @return * @return
@ -189,6 +204,24 @@ public class StpUtil {
} }
// =================== [临时过期] 验证相关 ===================
/**
* 检查当前token 是否已经[临时过期]如果已经过期则抛出异常
*/
public static void checkActivityTimeout() {
stpLogic.checkActivityTimeout();
}
/**
* 续签当前token( [最后操作时间] 更新为当前时间戳)
* <h1>请注意: 即时token已经 [临时过期] 也可续签成功
* 如果此场景下需要提示续签失败可在此之前调用 checkActivityTimeout() 强制检查是否过期即可 </h1>
*/
public static void updateLastActivityToNow() {
stpLogic.updateLastActivityToNow();
}
// =================== 过期时间相关 =================== // =================== 过期时间相关 ===================
@ -196,8 +229,8 @@ public class StpUtil {
* 获取当前登录者的token剩余有效时间 (单位: ) * 获取当前登录者的token剩余有效时间 (单位: )
* @return token剩余有效时间 * @return token剩余有效时间
*/ */
public long getTimeout() { public static long getTimeout() {
return stpLogic.getTimeout(); return stpLogic.getTokenTimeout();
} }
/** /**
@ -205,8 +238,42 @@ public class StpUtil {
* @param loginId 指定loginId * @param loginId 指定loginId
* @return token剩余有效时间 * @return token剩余有效时间
*/ */
public long getTimeoutByLoginId(Object loginId) { public static long getTimeoutByLoginId(Object loginId) {
return stpLogic.getTimeoutByLoginId(loginId); return stpLogic.getTokenTimeoutByLoginId(loginId);
}
/**
* 获取当前登录者的Session剩余有效时间 (单位: )
* @return token剩余有效时间
*/
public static long getSessionTimeout() {
return stpLogic.getSessionTimeout();
}
/**
* 获取指定loginId的Session剩余有效时间 (单位: )
* @param loginId 指定loginId
* @return token剩余有效时间
*/
public static long getSessionTimeoutByLoginId(Object loginId) {
return stpLogic.getSessionTimeoutByLoginId(loginId);
}
/**
* 获取当前token[临时过期]剩余有效时间 (单位: )
* @return token[临时过期]剩余有效时间
*/
public static long getTokenActivityTimeout() {
return stpLogic.getTokenActivityTimeout();
}
/**
* 获取指定token[临时过期]剩余有效时间 (单位: )
* @param tokenValue 指定token
* @return token[临时过期]剩余有效时间
*/
public static long getTokenActivityTimeoutByToken(String tokenValue) {
return stpLogic.getTokenActivityTimeoutByToken(tokenValue);
} }

View File

@ -38,6 +38,11 @@ public class SaTokenInsideUtil {
*/ */
public static final String JUST_CREATED_SAVE_KEY = "JUST_CREATED_SAVE_KEY_"; public static final String JUST_CREATED_SAVE_KEY = "JUST_CREATED_SAVE_KEY_";
/**
* 如果本次请求已经验证过[无操作过期], 则以此值存储在当前request中 TOKEN_ACTIVITY_TIMEOUT_CHECKED_KEY
*/
public static final String TOKEN_ACTIVITY_TIMEOUT_CHECKED_KEY = "TOKEN_ACTIVITY_TIMEOUT_CHECKED_KEY_";
/** /**
* 生成指定长度的随机字符串 * 生成指定长度的随机字符串
* @param length 字符串的长度 * @param length 字符串的长度