一、需求分析
如圖所示,一般網站的登錄界面都會有一個 [記住我] 按鈕,當你勾選它登錄後,即使你關閉瀏覽器再次打開網站,也依然會處於登錄狀態,無須重複驗證密碼:
本文將詳細介紹在 Sa-Token中,如何做到以下登錄模式:
- 記住我登錄:登錄後關閉瀏覽器,再次打開網站登錄狀態依然有效,無需重複登錄。
- 僅本次有效登錄:登錄後關閉瀏覽器,再次打開網站登錄狀態將失效,需要再次登錄。
- 七天免登錄:為登錄狀態設定一個詳細的有效期,在這個期限內無需重複登錄,過了期限後需要再次登錄。
Sa-Token 是一個輕量級 java 權限認證框架,主要解決登錄認證、權限認證、單點登錄、OAuth2、微服務網關鑑權 等一系列權限相關問題。
Gitee 開源地址:https://gitee.com/dromara/sa-token
首先在項目中引入 Sa-Token 依賴:
<!-- Sa-Token 權限認證 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.34.0</version>
</dependency>
注:如果你使用的是 SpringBoot 3.x,只需要將 sa-token-spring-boot-starter 修改為 sa-token-spring-boot3-starter 即可。
二、在 Sa-Token 中實現記住我功能
Sa-Token的登錄授權,默認就是[記住我]模式,為了實現[非記住我]模式,你需要在登錄時如下設置:
// 設置登錄賬號id為10001,第二個參數指定是否為[記住我],當此值為false後,關閉瀏覽器後再次打開需要重新登錄
StpUtil.login(10001, false);
那麼,Sa-Token實現[記住我]的具體原理是?
三、實現原理
Cookie作為瀏覽器提供的默認會話跟蹤機制,其生命週期有兩種形式,分別是:
- 臨時Cookie:有效期為本次會話,只要關閉瀏覽器窗口,Cookie就會消失。
- 持久Cookie:有效期為一個具體的時間,在時間未到期之前,即使用户關閉了瀏覽器Cookie也不會消失。
利用Cookie的此特性,我們便可以輕鬆實現 [記住我] 模式:
- 勾選 [記住我] 按鈕時:調用
StpUtil.login(10001, true),在瀏覽器寫入一個持久Cookie儲存 Token,此時用户即使重啓瀏覽器 Token 依然有效。 - 不勾選 [記住我] 按鈕時:調用
StpUtil.login(10001, false),在瀏覽器寫入一個臨時Cookie儲存 Token,此時用户在重啓瀏覽器後 Token 便會消失,導致會話失效。
動態演示圖:
四、前後端分離模式下如何實現[記住我]?
此時機智的你😏很快發現一個問題,Cookie雖好,卻無法在前後端分離環境下使用,那是不是代表上述方案在APP、小程序等環境中無效?
準確的講,答案是肯定的,任何基於Cookie的認證方案在前後端分離環境下都會失效(原因在於這些客户端默認沒有實現Cookie功能),不過好在,這些客户端一般都提供了替代方案,
唯一遺憾的是,此場景中token的生命週期需要我們在前端手動控制:
以經典跨端框架 uni-app 為例,我們可以使用如下方式達到同樣的效果:
// 使用本地存儲保存token,達到 [持久Cookie] 的效果
uni.setStorageSync("satoken", "xxxx-xxxx-xxxx-xxxx-xxx");
// 使用globalData保存token,達到 [臨時Cookie] 的效果
getApp().globalData.satoken = "xxxx-xxxx-xxxx-xxxx-xxx";
如果你決定在PC瀏覽器環境下進行前後端分離模式開發,那麼更加簡單:
// 使用 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!
五、登錄時指定 Token 有效期
登錄時不僅可以指定是否為[記住我]模式,還可以指定一個特定的時間作為 Token 有效時長,如下示例:
// 示例1:
// 指定token有效期(單位: 秒),如下所示token七天有效
StpUtil.login(10001, new SaLoginModel().setTimeout(60 * 60 * 24 * 7));
// ----------------------- 示例2:所有參數
// `SaLoginModel`為登錄參數Model,其有諸多參數決定登錄時的各種邏輯,例如:
StpUtil.login(10001, new SaLoginModel()
.setDevice("PC") // 此次登錄的客户端設備類型, 用於[同端互斥登錄]時指定此次登錄的設備類型
.setIsLastingCookie(true) // 是否為持久Cookie(臨時Cookie在瀏覽器關閉時會自動刪除,持久Cookie在重新打開後依然存在)
.setTimeout(60 * 60 * 24 * 7) // 指定此次登錄token的有效期, 單位:秒 (如未指定,自動取全局配置的 timeout 值)
.setToken("xxxx-xxxx-xxxx-xxxx") // 預定此次登錄的生成的Token
.setIsWriteHeader(false) // 是否在登錄後將 Token 寫入到響應頭
);
注:如果在登錄時未指定 new SaLoginModel().setTimeout(604800) 那麼框架將採用全局配置的 sa-token.timeout 值作為 Token 的有效期。
六、不同登錄策略的代碼對比
以下是三種登錄策略的代碼差異:
package com.pj.cases.up;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
/**
* Sa-Token 記住我模式登錄
*
* @author kong
* @since 2022-10-17
*/
@RestController
@RequestMapping("/RememberMe/")
public class RememberMeController {
// 記住我登錄 ---- http://localhost:8081/RememberMe/doLogin?name=zhang&pwd=123456
@RequestMapping("doLogin")
public SaResult doLogin(String name, String pwd) {
if("zhang".equals(name) && "123456".equals(pwd)) {
StpUtil.login(10001, true);
return SaResult.ok("登錄成功");
}
return SaResult.error("登錄失敗");
}
// 不記住我登錄 ---- http://localhost:8081/RememberMe/doLogin2?name=zhang&pwd=123456
@RequestMapping("doLogin2")
public SaResult doLogin2(String name, String pwd) {
if("zhang".equals(name) && "123456".equals(pwd)) {
StpUtil.login(10001, false);
return SaResult.ok("登錄成功");
}
return SaResult.error("登錄失敗");
}
// 七天免登錄 ---- http://localhost:8081/RememberMe/doLogin3?name=zhang&pwd=123456
@RequestMapping("doLogin3")
public SaResult doLogin3(String name, String pwd) {
if("zhang".equals(name) && "123456".equals(pwd)) {
StpUtil.login(10001, 60 * 60 * 24 * 7);
return SaResult.ok("登錄成功");
}
return SaResult.error("登錄失敗");
}
}
可依次訪問註釋中提供的測試鏈接,觀察不同登錄策略帶來的會話有效期差異。
參考資料
- Sa-Token 文檔:https://sa-token.cc
- Gitee 倉庫地址:https://gitee.com/dromara/sa-token
- GitHub 倉庫地址:https://github.com/dromara/sa-token