mirror of
https://gitee.com/dromara/sa-token.git
synced 2025-08-23 22:11:29 +08:00
完成 [记住我]模式 开发文档
This commit is contained in:
parent
e92ff9fb7d
commit
275b3cec6f
@ -2,6 +2,7 @@ package cn.dev33.satoken.stp;
|
|||||||
|
|
||||||
import cn.dev33.satoken.SaTokenManager;
|
import cn.dev33.satoken.SaTokenManager;
|
||||||
import cn.dev33.satoken.config.SaTokenConfig;
|
import cn.dev33.satoken.config.SaTokenConfig;
|
||||||
|
import cn.dev33.satoken.dao.SaTokenDao;
|
||||||
import cn.dev33.satoken.util.SaTokenConsts;
|
import cn.dev33.satoken.util.SaTokenConsts;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -23,9 +24,9 @@ public class SaLoginModel {
|
|||||||
public Long timeout;
|
public Long timeout;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否为临时Cookie(临时Cookie会在浏览器关闭时自动删除)
|
* 是否为持久Cookie(临时Cookie在浏览器关闭时会自动删除,持久Cookie在重新打开后依然存在)
|
||||||
*/
|
*/
|
||||||
public Boolean isTempCookie;
|
public Boolean isLastingCookie;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -61,21 +62,36 @@ public class SaLoginModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return isTempCookie
|
* @return isLastingCookie
|
||||||
*/
|
*/
|
||||||
public Boolean getIsTempCookie() {
|
public Boolean getIsLastingCookie() {
|
||||||
return isTempCookie;
|
return isLastingCookie;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param isTempCookie 要设置的 isTempCookie
|
* @param isLastingCookie 要设置的 isLastingCookie
|
||||||
* @return 对象自身
|
* @return 对象自身
|
||||||
*/
|
*/
|
||||||
public SaLoginModel setIsTempCookie(Boolean isTempCookie) {
|
public SaLoginModel setIsLastingCookie(Boolean isLastingCookie) {
|
||||||
this.isTempCookie = isTempCookie;
|
this.isLastingCookie = isLastingCookie;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return cookie时长
|
||||||
|
*/
|
||||||
|
public int getCookieTimeout() {
|
||||||
|
if(isLastingCookie == false) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(timeout == SaTokenDao.NEVER_EXPIRE) {
|
||||||
|
return Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
|
return (int)(long)timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建对象,初始化默认值
|
* 构建对象,初始化默认值
|
||||||
* @return 对象自身
|
* @return 对象自身
|
||||||
@ -96,8 +112,8 @@ public class SaLoginModel {
|
|||||||
if(timeout == null) {
|
if(timeout == null) {
|
||||||
timeout = config.getTimeout();
|
timeout = config.getTimeout();
|
||||||
}
|
}
|
||||||
if(isTempCookie == null) {
|
if(isLastingCookie == null) {
|
||||||
isTempCookie = false;
|
isLastingCookie = true;
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -161,10 +161,10 @@ public class StpLogic {
|
|||||||
/**
|
/**
|
||||||
* 在当前会话上登录id, 并指定登录设备
|
* 在当前会话上登录id, 并指定登录设备
|
||||||
* @param loginId 登录id,建议的类型:(long | int | String)
|
* @param loginId 登录id,建议的类型:(long | int | String)
|
||||||
* @param isTempCookie 是否为临时Cookie
|
* @param isLastingCookie 是否为持久Cookie
|
||||||
*/
|
*/
|
||||||
public void setLoginId(Object loginId, boolean isTempCookie) {
|
public void setLoginId(Object loginId, boolean isLastingCookie) {
|
||||||
setLoginId(loginId, new SaLoginModel().setIsTempCookie(isTempCookie));
|
setLoginId(loginId, new SaLoginModel().setIsLastingCookie(isLastingCookie));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -234,9 +234,8 @@ public class StpLogic {
|
|||||||
// 注入Cookie
|
// 注入Cookie
|
||||||
if(config.getIsReadCookie() == true){
|
if(config.getIsReadCookie() == true){
|
||||||
HttpServletResponse response = SaTokenManager.getSaTokenServlet().getResponse();
|
HttpServletResponse response = SaTokenManager.getSaTokenServlet().getResponse();
|
||||||
int cookieTimeout = loginModel.getIsTempCookie() ? -1 : (int)(long)loginModel.getTimeout();
|
|
||||||
SaTokenManager.getSaTokenCookie().addCookie(response, getTokenName(), tokenValue,
|
SaTokenManager.getSaTokenCookie().addCookie(response, getTokenName(), tokenValue,
|
||||||
"/", config.getCookieDomain(), cookieTimeout);
|
"/", config.getCookieDomain(), loginModel.getCookieTimeout());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -584,8 +583,9 @@ public class StpLogic {
|
|||||||
setLastActivityToNow(tokenValue);
|
setLastActivityToNow(tokenValue);
|
||||||
// cookie注入
|
// cookie注入
|
||||||
if(getConfig().getIsReadCookie() == true){
|
if(getConfig().getIsReadCookie() == true){
|
||||||
|
int cookieTimeout = (int)(getConfig().getTimeout() == SaTokenDao.NEVER_EXPIRE ? Integer.MAX_VALUE : getConfig().getTimeout());
|
||||||
SaTokenManager.getSaTokenCookie().addCookie(SaTokenManager.getSaTokenServlet().getResponse(), getTokenName(), tokenValue,
|
SaTokenManager.getSaTokenCookie().addCookie(SaTokenManager.getSaTokenServlet().getResponse(), getTokenName(), tokenValue,
|
||||||
"/", getConfig().getCookieDomain(), (int)getConfig().getTimeout());
|
"/", getConfig().getCookieDomain(), cookieTimeout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,10 +75,10 @@ public class StpUtil {
|
|||||||
/**
|
/**
|
||||||
* 在当前会话上登录id, 并指定登录设备
|
* 在当前会话上登录id, 并指定登录设备
|
||||||
* @param loginId 登录id,建议的类型:(long | int | String)
|
* @param loginId 登录id,建议的类型:(long | int | String)
|
||||||
* @param isTempCookie 是否为临时Cookie
|
* @param isLastingCookie 是否为持久Cookie
|
||||||
*/
|
*/
|
||||||
public void setLoginId(Object loginId, boolean isTempCookie) {
|
public static void setLoginId(Object loginId, boolean isLastingCookie) {
|
||||||
stpLogic.setLoginId(loginId, isTempCookie);
|
stpLogic.setLoginId(loginId, isLastingCookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,7 +14,6 @@ import com.pj.util.AjaxJson;
|
|||||||
import cn.dev33.satoken.exception.NotLoginException;
|
import cn.dev33.satoken.exception.NotLoginException;
|
||||||
import cn.dev33.satoken.exception.NotPermissionException;
|
import cn.dev33.satoken.exception.NotPermissionException;
|
||||||
import cn.dev33.satoken.exception.NotRoleException;
|
import cn.dev33.satoken.exception.NotRoleException;
|
||||||
import cn.dev33.satoken.stp.StpUtil;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 全局异常处理
|
* 全局异常处理
|
||||||
@ -25,7 +24,7 @@ public class GlobalException {
|
|||||||
// 在当前类每个方法进入之前触发的操作
|
// 在当前类每个方法进入之前触发的操作
|
||||||
@ModelAttribute
|
@ModelAttribute
|
||||||
public void get(HttpServletRequest request) throws IOException {
|
public void get(HttpServletRequest request) throws IOException {
|
||||||
StpUtil.checkPermission("user:add");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -246,9 +246,10 @@ public class TestController {
|
|||||||
// StpUtil.getTokenSession().logout();
|
// StpUtil.getTokenSession().logout();
|
||||||
// StpUtil.logoutByLoginId(10001);
|
// StpUtil.logoutByLoginId(10001);
|
||||||
// StpUtil.setLoginId(10001);
|
// StpUtil.setLoginId(10001);
|
||||||
// StpUtil.setLoginId(10001, new SaLoginModel().setIsTempCookie(true));
|
// StpUtil.setLoginId(10001, false);
|
||||||
// StpUtil.getLoginId();
|
// StpUtil.getLoginId();
|
||||||
|
// StpUtil.setLoginId(10001);
|
||||||
|
// StpUtil.getTokenSession();
|
||||||
return AjaxJson.getSuccess("访问成功");
|
return AjaxJson.getSuccess("访问成功");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,9 +10,52 @@
|
|||||||
|
|
||||||
### 在sa-token中实现记住我功能
|
### 在sa-token中实现记住我功能
|
||||||
|
|
||||||
sa-token的登录授权,默认就是`记住我`模式,为了实现`非记住我`模式, 你需要做一些适配
|
sa-token的登录授权,**默认就是`[记住我]`模式**,为了实现`[非记住我]`模式, 你需要在登录时如下设置:
|
||||||
|
|
||||||
要
|
``` java
|
||||||
|
// 设置登录账号id为10001,第二个参数指定是否为[记住我],当此值为false后,关闭浏览器后再次打开需要重新登录
|
||||||
|
StpUtil.setLoginId(10001, false);
|
||||||
|
```
|
||||||
|
|
||||||
|
那么,sa-token实现`[记住我]`的具体原理是?
|
||||||
|
|
||||||
|
|
||||||
|
### 实现原理
|
||||||
|
Cookie作为浏览器提供的默认会话跟踪机制,其生命周期有两种形式,分别是:
|
||||||
|
- 临时Cookie:有效期为本次会话,只要关闭浏览器窗口,Cookie就会消失
|
||||||
|
- 永久Cookie:有效期为一个具体的时间,在时间未到期之前,即使用户关闭了浏览器Cookie也不会消失
|
||||||
|
|
||||||
|
利用Cookie的此特性,我们便可以轻松实现 [记住我] 模式:
|
||||||
|
- 勾选[记住我]按钮时:调用`StpUtil.setLoginId(10001, true)`,在浏览器写入一个`永久Cookie`保存token,此时用户即使重启浏览器token依然有效
|
||||||
|
- 不勾选[记住我]按钮时:调用`StpUtil.setLoginId(10001, false)`,在浏览器写入一个`临时Cookie`保存token,此时用户在重启浏览器后token便会消失,导致会话失效
|
||||||
|
|
||||||
|
|
||||||
|
### 前后台分离模式下如何实现[记住我]?
|
||||||
|
|
||||||
|
此时机智的你😏很快发现一个问题,Cookie虽好,却无法在前后端分离环境下使用,那是不是代表上述方案在APP、小程序等环境中无效?
|
||||||
|
|
||||||
|
准确的讲,答案是肯定的,任何基于Cookie的认证方案在前后台分离环境下都会失效(原因在于这些客户端默认没有实现Cookie功能),不过好在,这些客户端一般都提供了替代方案,
|
||||||
|
唯一遗憾的是,此场景中token的生命周期需要我们在前端手动控制
|
||||||
|
|
||||||
|
以经典跨端框架 [uni-app](https://uniapp.dcloud.io/) 为例,我们可以使用如下方式达到同样的效果:
|
||||||
|
``` js
|
||||||
|
// 使用本地存储保存token,达到 [永久Cookie] 的效果
|
||||||
|
uni.setStorageSync("satoken", "xxxx-xxxx-xxxx-xxxx-xxx");
|
||||||
|
|
||||||
|
// 使用globalData保存token,达到 [临时Cookie] 的效果
|
||||||
|
getApp().globalData.satoken = "xxxx-xxxx-xxxx-xxxx-xxx";
|
||||||
|
```
|
||||||
|
|
||||||
|
如果你决定在PC浏览器环境下进行前后台分离模式开发,那么更加简单:
|
||||||
|
``` js
|
||||||
|
// 使用 localStorage 保存token,达到 [永久Cookie] 的效果
|
||||||
|
localStorage.setItem("satoken", "xxxx-xxxx-xxxx-xxxx-xxx");
|
||||||
|
|
||||||
|
// 使用 sessionStorage 保存token,达到 [临时Cookie] 的效果
|
||||||
|
sessionStorage.setItem("satoken", "xxxx-xxxx-xxxx-xxxx-xxx");
|
||||||
|
```
|
||||||
|
|
||||||
|
Remember me, it's too easy!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user