diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/config/SaTokenConfig.java b/sa-token-core/src/main/java/cn/dev33/satoken/config/SaTokenConfig.java index 6c663e5e..34cce1e4 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/config/SaTokenConfig.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/config/SaTokenConfig.java @@ -66,9 +66,11 @@ public class SaTokenConfig { /** * @param tokenName token名称 (同时也是cookie名称) + * @return 对象自身 */ - public void setTokenName(String tokenName) { + public SaTokenConfig setTokenName(String tokenName) { this.tokenName = tokenName; + return this; } /** @@ -80,9 +82,11 @@ public class SaTokenConfig { /** * @param timeout token的长久有效期(单位:秒) 默认30天, -1代表永久 + * @return 对象自身 */ - public void setTimeout(long timeout) { + public SaTokenConfig setTimeout(long timeout) { this.timeout = timeout; + return this; } /** @@ -96,9 +100,11 @@ public class SaTokenConfig { /** * @param activityTimeout token临时有效期 [指定时间内无操作就视为token过期] (单位: 秒), 默认-1 代表不限制 * (例如可以设置为1800代表30分钟内无操作就过期) + * @return 对象自身 */ - public void setActivityTimeout(long activityTimeout) { + public SaTokenConfig setActivityTimeout(long activityTimeout) { this.activityTimeout = activityTimeout; + return this; } /** @@ -110,9 +116,11 @@ public class SaTokenConfig { /** * @param allowConcurrentLogin 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录) + * @return 对象自身 */ - public void setAllowConcurrentLogin(Boolean allowConcurrentLogin) { + public SaTokenConfig setAllowConcurrentLogin(Boolean allowConcurrentLogin) { this.allowConcurrentLogin = allowConcurrentLogin; + return this; } /** @@ -124,9 +132,11 @@ public class SaTokenConfig { /** * @param isShare 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token) + * @return 对象自身 */ - public void setIsShare(Boolean isShare) { + public SaTokenConfig setIsShare(Boolean isShare) { this.isShare = isShare; + return this; } /** @@ -138,9 +148,11 @@ public class SaTokenConfig { /** * @param isReadBody 是否尝试从请求体里读取token + * @return 对象自身 */ - public void setIsReadBody(Boolean isReadBody) { + public SaTokenConfig setIsReadBody(Boolean isReadBody) { this.isReadBody = isReadBody; + return this; } /** @@ -152,9 +164,11 @@ public class SaTokenConfig { /** * @param isReadHead 是否尝试从header里读取token + * @return 对象自身 */ - public void setIsReadHead(Boolean isReadHead) { + public SaTokenConfig setIsReadHead(Boolean isReadHead) { this.isReadHead = isReadHead; + return this; } /** @@ -166,9 +180,11 @@ public class SaTokenConfig { /** * @param isReadCookie 是否尝试从cookie里读取token + * @return 对象自身 */ - public void setIsReadCookie(Boolean isReadCookie) { + public SaTokenConfig setIsReadCookie(Boolean isReadCookie) { this.isReadCookie = isReadCookie; + return this; } /** @@ -180,9 +196,11 @@ public class SaTokenConfig { /** * @param tokenStyle token风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik) + * @return 对象自身 */ - public void setTokenStyle(String tokenStyle) { + public SaTokenConfig setTokenStyle(String tokenStyle) { this.tokenStyle = tokenStyle; + return this; } /** @@ -195,9 +213,11 @@ public class SaTokenConfig { /** * @param dataRefreshPeriod 默认dao层实现类中,每次清理过期数据间隔的时间 (单位: 秒) * ,默认值30秒,设置为-1代表不启动定时清理 + * @return 对象自身 */ - public void setDataRefreshPeriod(int dataRefreshPeriod) { + public SaTokenConfig setDataRefreshPeriod(int dataRefreshPeriod) { this.dataRefreshPeriod = dataRefreshPeriod; + return this; } /** @@ -210,9 +230,11 @@ public class SaTokenConfig { /** * @param tokenSessionCheckLogin 获取[token专属session]时是否必须登录 * (如果配置为true,会在每次获取[token-session]时校验是否登录) + * @return 对象自身 */ - public void setTokenSessionCheckLogin(Boolean tokenSessionCheckLogin) { + public SaTokenConfig setTokenSessionCheckLogin(Boolean tokenSessionCheckLogin) { this.tokenSessionCheckLogin = tokenSessionCheckLogin; + return this; } /** @@ -224,9 +246,11 @@ public class SaTokenConfig { /** * @param autoRenew 是否打开自动续签 (如果此值为true, 框架会在每次直接或间接调用getLoginId()时进行一次过期检查与续签操作) + * @return 对象自身 */ - public void setAutoRenew(Boolean autoRenew) { + public SaTokenConfig setAutoRenew(Boolean autoRenew) { this.autoRenew = autoRenew; + return this; } /** @@ -238,9 +262,11 @@ public class SaTokenConfig { /** * @param cookieDomain 写入Cookie时显式指定的作用域, 常用于单点登录二级域名共享Cookie的场景 + * @return 对象自身 */ - public void setCookieDomain(String cookieDomain) { + public SaTokenConfig setCookieDomain(String cookieDomain) { this.cookieDomain = cookieDomain; + return this; } /** @@ -252,9 +278,11 @@ public class SaTokenConfig { /** * @param isV 是否在初始化配置时打印版本字符画 + * @return 对象自身 */ - public void setIsV(Boolean isV) { + public SaTokenConfig setIsV(Boolean isV) { this.isV = isV; + return this; } 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 new file mode 100644 index 00000000..35b81bf2 --- /dev/null +++ b/sa-token-core/src/main/java/cn/dev33/satoken/stp/SaLoginModel.java @@ -0,0 +1,115 @@ +package cn.dev33.satoken.stp; + +import cn.dev33.satoken.SaTokenManager; +import cn.dev33.satoken.config.SaTokenConfig; +import cn.dev33.satoken.util.SaTokenConsts; + +/** + * 调用StpUtil.setLogin()时的配置 Model + * @author kong + * + */ +public class SaLoginModel { + + + /** + * 此次登录的客户端设备标识 + */ + public String device; + + /** + * 指定此次登录token的有效期, 单位:秒 (如未指定,自动取全局配置的timeout值) + */ + public Long timeout; + + /** + * 是否为临时Cookie(临时Cookie会在浏览器关闭时自动删除) + */ + public Boolean isTempCookie; + + + /** + * @return device + */ + public String getDevice() { + return device; + } + + /** + * @param device 要设置的 device + * @return 对象自身 + */ + public SaLoginModel setDevice(String device) { + this.device = device; + return this; + } + + /** + * @return timeout + */ + public Long getTimeout() { + return timeout; + } + + /** + * @param timeout 要设置的 timeout + * @return 对象自身 + */ + public SaLoginModel setTimeout(long timeout) { + this.timeout = timeout; + return this; + } + + /** + * @return isTempCookie + */ + public Boolean getIsTempCookie() { + return isTempCookie; + } + + /** + * @param isTempCookie 要设置的 isTempCookie + * @return 对象自身 + */ + public SaLoginModel setIsTempCookie(Boolean isTempCookie) { + this.isTempCookie = isTempCookie; + return this; + } + + /** + * 构建对象,初始化默认值 + * @return 对象自身 + */ + public SaLoginModel build() { + return build(SaTokenManager.getConfig()); + } + + /** + * 构建对象,初始化默认值 + * @param config 配置对象 + * @return 对象自身 + */ + public SaLoginModel build(SaTokenConfig config) { + if(device == null) { + device = SaTokenConsts.DEFAULT_LOGIN_DEVICE; + } + if(timeout == null) { + timeout = config.getTimeout(); + } + if(isTempCookie == null) { + isTempCookie = false; + } + return this; + } + + + /** + * 静态方法获取一个 SaLoginModel 对象 + * @return SaLoginModel 对象 + */ + public static SaLoginModel create() { + return new SaLoginModel(); + } + + +} 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 d1cce6ac..aec7c63d 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 @@ -8,6 +8,7 @@ import java.util.Objects; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import cn.dev33.satoken.SaTokenManager; import cn.dev33.satoken.annotation.SaCheckLogin; @@ -145,20 +146,39 @@ public class StpLogic { * @param loginId 登录id,建议的类型:(long | int | String) */ public void setLoginId(Object loginId) { - setLoginId(loginId, SaTokenConsts.DEFAULT_LOGIN_DEVICE); + setLoginId(loginId, new SaLoginModel()); } - + /** - * 在当前会话上登录id + * 在当前会话上登录id, 并指定登录设备 * @param loginId 登录id,建议的类型:(long | int | String) * @param device 设备标识 */ public void setLoginId(Object loginId, String device) { + setLoginId(loginId, new SaLoginModel().setDevice(device)); + } + + /** + * 在当前会话上登录id, 并指定登录设备 + * @param loginId 登录id,建议的类型:(long | int | String) + * @param isTempCookie 是否为临时Cookie + */ + public void setLoginId(Object loginId, boolean isTempCookie) { + setLoginId(loginId, new SaLoginModel().setIsTempCookie(isTempCookie)); + } + + /** + * 在当前会话上登录id, 并指定所有登录参数Model + * @param loginId 登录id,建议的类型:(long | int | String) + * @param loginModel 此次登录的参数Model + */ + public void setLoginId(Object loginId, SaLoginModel loginModel) { // ------ 1、获取相应对象 HttpServletRequest request = SaTokenManager.getSaTokenServlet().getRequest(); SaTokenConfig config = getConfig(); SaTokenDao dao = SaTokenManager.getSaTokenDao(); + loginModel.build(config); // ------ 2、生成一个token String tokenValue = null; @@ -166,7 +186,7 @@ public class StpLogic { if(config.getAllowConcurrentLogin() == true) { // 如果配置为共享token, 则尝试从Session签名记录里取出token if(config.getIsShare() == true) { - tokenValue = getTokenValueByLoginId(loginId, device); + tokenValue = getTokenValueByLoginId(loginId, loginModel.getDevice()); } } else { // --- 如果不允许并发登录 @@ -175,7 +195,7 @@ public class StpLogic { if(session != null) { List tokenSignList = session.getTokenSignList(); for (TokenSign tokenSign : tokenSignList) { - if(tokenSign.getDevice().equals(device)) { + if(tokenSign.getDevice().equals(loginModel.getDevice())) { // 1. 将此token 标记为已顶替 dao.update(getKeyTokenValue(tokenSign.getValue()), NotLoginException.BE_REPLACED); // 2. 清理掉[token-最后操作时间] @@ -196,22 +216,27 @@ public class StpLogic { if(session == null) { session = getSessionByLoginId(loginId); } else { - dao.updateSessionTimeout(session.getId(), config.getTimeout()); + // 保证此Session的有效期 >= token的有效期 + if(dao.getSessionTimeout(session.getId()) < loginModel.getTimeout()) { + dao.updateSessionTimeout(session.getId(), loginModel.getTimeout()); + } } // 在session上记录token签名 - session.addTokenSign(new TokenSign(tokenValue, device)); + session.addTokenSign(new TokenSign(tokenValue, loginModel.getDevice())); // ------ 4. 持久化其它数据 // token -> uid - dao.set(getKeyTokenValue(tokenValue), String.valueOf(loginId), config.getTimeout()); + dao.set(getKeyTokenValue(tokenValue), String.valueOf(loginId), loginModel.getTimeout()); // 将token保存到本次request里 request.setAttribute(getKeyJustCreatedSave(), tokenValue); // 写入 [最后操作时间] setLastActivityToNow(tokenValue); - // cookie注入 - if(config.getIsReadCookie() == true){ - SaTokenManager.getSaTokenCookie().addCookie(SaTokenManager.getSaTokenServlet().getResponse(), getTokenName(), tokenValue, - "/", config.getCookieDomain(), (int)config.getTimeout()); + // 注入Cookie + if(config.getIsReadCookie() == true){ + HttpServletResponse response = SaTokenManager.getSaTokenServlet().getResponse(); + int cookieTimeout = loginModel.getIsTempCookie() ? -1 : (int)(long)loginModel.getTimeout(); + SaTokenManager.getSaTokenCookie().addCookie(response, getTokenName(), tokenValue, + "/", config.getCookieDomain(), cookieTimeout); } } 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 d69af20f..b46b7ec5 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 @@ -64,13 +64,31 @@ public class StpUtil { } /** - * 在当前会话上登录id + * 在当前会话上登录id, 并指定登录设备 * @param loginId 登录id,建议的类型:(long | int | String) * @param device 设备标识 */ public static void setLoginId(Object loginId, String device) { stpLogic.setLoginId(loginId, device); } + + /** + * 在当前会话上登录id, 并指定登录设备 + * @param loginId 登录id,建议的类型:(long | int | String) + * @param isTempCookie 是否为临时Cookie + */ + public void setLoginId(Object loginId, boolean isTempCookie) { + stpLogic.setLoginId(loginId, isTempCookie); + } + + /** + * 在当前会话上登录id, 并指定所有登录参数Model + * @param loginId 登录id,建议的类型:(long | int | String) + * @param loginModel 此次登录的参数Model + */ + public static void setLoginId(Object loginId, SaLoginModel loginModel) { + stpLogic.setLoginId(loginId, loginModel); + } /** * 当前会话注销登录 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 92852962..756c5599 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 @@ -3,6 +3,8 @@ package com.pj.test; import java.util.Date; import java.util.List; +import javax.servlet.http.HttpServletResponse; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -18,6 +20,7 @@ import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.annotation.SaCheckRole; import cn.dev33.satoken.annotation.SaMode; import cn.dev33.satoken.session.SaSessionCustomUtil; +import cn.dev33.satoken.stp.SaLoginModel; import cn.dev33.satoken.stp.SaTokenInfo; import cn.dev33.satoken.stp.StpUtil; @@ -244,10 +247,13 @@ public class TestController { // 测试 浏览器访问: http://localhost:8081/test/test @RequestMapping("test") - public AjaxJson test() { + public AjaxJson test(HttpServletResponse response) { // StpUtil.getTokenSession().logout(); // StpUtil.logoutByLoginId(10001); +// StpUtil.setLoginId(10001); + StpUtil.setLoginId(10001, new SaLoginModel().setIsTempCookie(true)); StpUtil.getLoginId(); + return AjaxJson.getSuccess(); }