中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

SpringBoot怎么使用Sa-Token實現登錄認證

發布時間:2023-04-07 11:34:14 來源:億速云 閱讀:198 作者:iii 欄目:開發技術

這篇文章主要講解了“SpringBoot怎么使用Sa-Token實現登錄認證”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“SpringBoot怎么使用Sa-Token實現登錄認證”吧!

一、設計思路

對于一些登錄之后才能訪問的接口(例如:查詢我的賬號資料),我們通常的做法是增加一層接口校驗:

  • 如果校驗通過,則:正常返回數據。

  • 如果校驗未通過,則:拋出異常,告知其需要先進行登錄。

那么,判斷會話是否登錄的依據是什么?我們先來簡單分析一下登錄訪問流程:

  • 用戶提交 name + password 參數,調用登錄接口。

  • 登錄成功,返回這個用戶的 Token 會話憑證。

  • 用戶后續的每次請求,都攜帶上這個 Token。

  • 服務器根據 Token 判斷此會話是否登錄成功。

所謂登錄認證,指的就是服務器校驗賬號密碼,為用戶頒發 Token 會話憑證的過程,這個 Token 也是我們后續判斷會話是否登錄的關鍵所在。

動態圖演示:

SpringBoot怎么使用Sa-Token實現登錄認證

接下來,我們將介紹在 SpringBoot 中如何使用 Sa-Token 完成登錄認證操作。

Sa-Token 是一個 java 權限認證框架,主要解決登錄認證、權限認證、單點登錄、OAuth3、微服務網關鑒權 等一系列權限相關問題。
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 即可。

二、登錄與注銷

根據以上思路,我們需要一個會話登錄的函數:

// 會話登錄:參數填寫要登錄的賬號id,建議的數據類型:long | int | String, 不可以傳入復雜類型,如:User、Admin 等等
StpUtil.login(Object id);

只此一句代碼,便可以使會話登錄成功,實際上,Sa-Token 在背后做了大量的工作,包括但不限于:

  • 檢查此賬號是否之前已有登錄

  • 為賬號生成 Token 憑證與 Session 會話

  • 通知全局偵聽器,xx 賬號登錄成功

  • 將 Token 注入到請求上下文

  • 等等其它工作&hellip;&hellip;

你暫時不需要完整的了解整個登錄過程,你只需要記住關鍵一點:Sa-Token 為這個賬號創建了一個Token憑證,且通過 Cookie 上下文返回給了前端。

所以一般情況下,我們的登錄接口代碼,會大致類似如下:

// 會話登錄接口 
@RequestMapping("doLogin")
public SaResult doLogin(String name, String pwd) {
    // 第一步:比對前端提交的賬號名稱、密碼
    if("zhang".equals(name) && "123456".equals(pwd)) {
        // 第二步:根據賬號id,進行登錄 
        StpUtil.login(10001);
        return SaResult.ok("登錄成功");
    }
    return SaResult.error("登錄失敗");
}

如果你對以上代碼閱讀沒有壓力,你可能會注意到略顯奇怪的一點:此處僅僅做了會話登錄,但并沒有主動向前端返回 Token 信息。
是因為不需要嗎?嚴格來講是需要的,只不過 StpUtil.login(id) 方法利用了 Cookie 自動注入的特性,省略了你手寫返回 Token 的代碼。

如果你對 Cookie 功能還不太了解,也不用擔心,我們會在之后的 [ 前后端分離 ] 章節中詳細的闡述 Cookie 功能,現在你只需要了解最基本的兩點:

  • Cookie 可以從后端控制往瀏覽器中寫入 Token 值。

  • Cookie 會在前端每次發起請求時自動提交 Token 值。

因此,在 Cookie 功能的加持下,我們可以僅靠 StpUtil.login(id) 一句代碼就完成登錄認證。

除了登錄方法,我們還需要:

// 當前會話注銷登錄
StpUtil.logout();

// 獲取當前會話是否已經登錄,返回true=已登錄,false=未登錄
StpUtil.isLogin();

// 檢驗當前會話是否已經登錄, 如果未登錄,則拋出異常:`NotLoginException`
StpUtil.checkLogin();

異常 NotLoginException 代表當前會話暫未登錄,可能的原因有很多:
前端沒有提交 Token、前端提交的 Token 是無效的、前端提交的 Token 已經過期 &hellip;&hellip; 等等。

Sa-Token 未登錄場景值參照表:

場景值對應常量含義說明
-1NotLoginException.NOT_TOKEN未能從請求中讀取到 Token
-2NotLoginException.INVALID_TOKEN已讀取到 Token,但是 Token無效
-3NotLoginException.TOKEN_TIMEOUT已讀取到 Token,但是 Token已經過期
-4NotLoginException.BE_REPLACED已讀取到 Token,但是 Token 已被頂下線
-5NotLoginException.KICK_OUT已讀取到 Token,但是 Token 已被踢下線

那么,如何獲取場景值呢?廢話少說直接上代碼:

// 全局異常攔截(攔截項目中的NotLoginException異常)
@ExceptionHandler(NotLoginException.class)
public SaResult handlerNotLoginException(NotLoginException nle)
        throws Exception {

    // 打印堆棧,以供調試
    nle.printStackTrace(); 
    
    // 判斷場景值,定制化異常信息 
    String message = "";
    if(nle.getType().equals(NotLoginException.NOT_TOKEN)) {
        message = "未提供token";
    }
    else if(nle.getType().equals(NotLoginException.INVALID_TOKEN)) {
        message = "token無效";
    }
    else if(nle.getType().equals(NotLoginException.TOKEN_TIMEOUT)) {
        message = "token已過期";
    }
    else if(nle.getType().equals(NotLoginException.BE_REPLACED)) {
        message = "token已被頂下線";
    }
    else if(nle.getType().equals(NotLoginException.KICK_OUT)) {
        message = "token已被踢下線";
    }
    else {
        message = "當前會話未登錄";
    }
    
    // 返回給前端
    return SaResult.error(message);
}

注意:以上代碼并非處理邏輯的最佳方式,只為以最簡單的代碼演示出場景值的獲取與應用,大家可以根據自己的項目需求來定制化處理

三、會話查詢

// 獲取當前會話賬號id, 如果未登錄,則拋出異常:`NotLoginException`
StpUtil.getLoginId();

// 類似查詢API還有:
StpUtil.getLoginIdAsString();    // 獲取當前會話賬號id, 并轉化為`String`類型
StpUtil.getLoginIdAsInt();       // 獲取當前會話賬號id, 并轉化為`int`類型
StpUtil.getLoginIdAsLong();      // 獲取當前會話賬號id, 并轉化為`long`類型

// ---------- 指定未登錄情形下返回的默認值 ----------

// 獲取當前會話賬號id, 如果未登錄,則返回null 
StpUtil.getLoginIdDefaultNull();

// 獲取當前會話賬號id, 如果未登錄,則返回默認值 (`defaultValue`可以為任意類型)
StpUtil.getLoginId(T defaultValue);

四、Token 查詢

// 獲取當前會話的token值
StpUtil.getTokenValue();

// 獲取當前`StpLogic`的token名稱
StpUtil.getTokenName();

// 獲取指定token對應的賬號id,如果未登錄,則返回 null
StpUtil.getLoginIdByToken(String tokenValue);

// 獲取當前會話剩余有效期(單位:s,返回-1代表永久有效)
StpUtil.getTokenTimeout();

// 獲取當前會話的token信息參數
StpUtil.getTokenInfo();

TokenInfo 是 Token 信息 Model,用來描述一個 Token 的常用參數:

{
    "tokenName": "satoken",           // token名稱
    "tokenValue": "e67b99f1-3d7a-4a8d-bb2f-e888a0805633",      // token值
    "isLogin": true,                  // 此token是否已經登錄
    "loginId": "10001",               // 此token對應的LoginId,未登錄時為null
    "loginType": "login",              // 賬號類型標識
    "tokenTimeout": 2591977,          // token剩余有效期 (單位: 秒)
    "sessionTimeout": 2591977,        // User-Session剩余有效時間 (單位: 秒)
    "tokenSessionTimeout": -2,        // Token-Session剩余有效時間 (單位: 秒) (-2表示系統中不存在這個緩存)
    "tokenActivityTimeout": -1,       // token剩余無操作有效時間 (單位: 秒)
    "loginDevice": "default-device"   // 登錄設備類型 
}

五、來個小測試,加深一下理解

新建 LoginAuthController,復制以下代碼

package com.pj.cases.use;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;

/**
 * Sa-Token 登錄認證示例 
 * 
 * @author kong
 * @since 2022-10-13
 */
@RestController
@RequestMapping("/acc/")
public class LoginAuthController {

    // 會話登錄接口  ---- http://localhost:8081/acc/doLogin?name=zhang&pwd=123456
    @RequestMapping("doLogin")
    public SaResult doLogin(String name, String pwd) {
        
        // 第一步:比對前端提交的 賬號名稱 & 密碼 是否正確,比對成功后開始登錄 
        //         此處僅作模擬示例,真實項目需要從數據庫中查詢數據進行比對 
        if("zhang".equals(name) && "123456".equals(pwd)) {
            
            // 第二步:根據賬號id,進行登錄 
            //         此處填入的參數應該保持用戶表唯一,比如用戶id,不可以直接填入整個 User 對象 
            StpUtil.login(10001);
            
            // SaResult 是 Sa-Token 中對返回結果的簡單封裝,下面的示例將不再贅述 
            return SaResult.ok("登錄成功");
        }
        
        return SaResult.error("登錄失敗");
    }

    // 查詢當前登錄狀態  ---- http://localhost:8081/acc/isLogin
    @RequestMapping("isLogin")
    public SaResult isLogin() {
        // StpUtil.isLogin() 查詢當前客戶端是否登錄,返回 true 或 false 
        boolean isLogin = StpUtil.isLogin();
        return SaResult.ok("當前客戶端是否登錄:" + isLogin);
    }

    // 校驗當前登錄狀態  ---- http://localhost:8081/acc/checkLogin
    @RequestMapping("checkLogin")
    public SaResult checkLogin() {
        // 檢驗當前會話是否已經登錄, 如果未登錄,則拋出異常:`NotLoginException`
        StpUtil.checkLogin();

        // 拋出異常后,代碼將走入全局異常處理(GlobalException.java),如果沒有拋出異常,則代表通過了登錄校驗,返回下面信息 
        return SaResult.ok("校驗登錄成功,這行字符串是只有登錄后才會返回的信息");
    }

    // 獲取當前登錄的賬號是誰  ---- http://localhost:8081/acc/getLoginId
    @RequestMapping("getLoginId")
    public SaResult getLoginId() {
        // 需要注意的是,StpUtil.getLoginId() 自帶登錄校驗效果
        // 也就是說如果在未登錄的情況下調用這句代碼,框架就會拋出 `NotLoginException` 異常,效果和 StpUtil.checkLogin() 是一樣的 
        Object userId = StpUtil.getLoginId();
        System.out.println("當前登錄的賬號id是:" + userId);
        
        // 如果不希望 StpUtil.getLoginId() 觸發登錄校驗效果,可以填入一個默認值
        // 如果會話未登錄,則返回這個默認值,如果會話已登錄,將正常返回登錄的賬號id 
        Object userId2 = StpUtil.getLoginId(0);
        System.out.println("當前登錄的賬號id是:" + userId2);
        
        // 或者使其在未登錄的時候返回 null 
        Object userId3 = StpUtil.getLoginIdDefaultNull();
        System.out.println("當前登錄的賬號id是:" + userId3);
        
        // 類型轉換:
        // StpUtil.getLoginId() 返回的是 Object 類型,你可以使用以下方法指定其返回的類型 
        int userId4 = StpUtil.getLoginIdAsInt();  // 將返回值轉換為 int 類型 
        long userId5 = StpUtil.getLoginIdAsLong();  // 將返回值轉換為 long 類型 
        String userId6 = StpUtil.getLoginIdAsString();  // 將返回值轉換為 String 類型 
        
        // 疑問:數據基本類型不是有八個嗎,為什么只封裝以上三種類型的轉換?
        // 因為大多數項目都是拿 int、long 或 String 聲明 UserId 的類型的,實在沒見過哪個項目用 double、float、boolean 之類來聲明 UserId 
        System.out.println("當前登錄的賬號id是:" + userId4 + " --- " + userId5 + " --- " + userId6);
        
        // 返回給前端 
        return SaResult.ok("當前客戶端登錄的賬號id是:" + userId);
    }

    // 查詢 Token 信息  ---- http://localhost:8081/acc/tokenInfo
    @RequestMapping("tokenInfo")
    public SaResult tokenInfo() {
        // TokenName 是 Token 名稱的意思,此值也決定了前端提交 Token 時應該使用的參數名稱 
        String tokenName = StpUtil.getTokenName();
        System.out.println("前端提交 Token 時應該使用的參數名稱:" + tokenName);
        
        // 使用 StpUtil.getTokenValue() 獲取前端提交的 Token 值 
        // 框架默認前端可以從以下三個途徑中提交 Token:
        //         Cookie         (瀏覽器自動提交)
        //         Header頭    (代碼手動提交)
        //         Query 參數    (代碼手動提交) 例如: /user/getInfo?satoken=xxxx-xxxx-xxxx-xxxx 
        // 讀取順序為: Query 參數 --> Header頭 -- > Cookie 
        // 以上三個地方都讀取不到 Token 信息的話,則視為前端沒有提交 Token 
        String tokenValue = StpUtil.getTokenValue();
        System.out.println("前端提交的Token值為:" + tokenValue);
        
        // TokenInfo 包含了此 Token 的大多數信息 
        SaTokenInfo info = StpUtil.getTokenInfo();
        System.out.println("Token 名稱:" + info.getTokenName());
        System.out.println("Token 值:" + info.getTokenValue());
        System.out.println("當前是否登錄:" + info.getIsLogin());
        System.out.println("當前登錄的賬號id:" + info.getLoginId());
        System.out.println("當前登錄賬號的類型:" + info.getLoginType());
        System.out.println("當前登錄客戶端的設備類型:" + info.getLoginDevice());
        System.out.println("當前 Token 的剩余有效期:" + info.getTokenTimeout()); // 單位:秒,-1代表永久有效,-2代表值不存在
        System.out.println("當前 Token 的剩余臨時有效期:" + info.getTokenActivityTimeout()); // 單位:秒,-1代表永久有效,-2代表值不存在
        System.out.println("當前 User-Session 的剩余有效期" + info.getSessionTimeout()); // 單位:秒,-1代表永久有效,-2代表值不存在
        System.out.println("當前 Token-Session 的剩余有效期" + info.getTokenSessionTimeout()); // 單位:秒,-1代表永久有效,-2代表值不存在
        
        // 返回給前端 
        return SaResult.data(StpUtil.getTokenInfo());
    }
    
    // 會話注銷  ---- http://localhost:8081/acc/logout
    @RequestMapping("logout")
    public SaResult logout() {
        // 退出登錄會清除三個地方的數據:
        //         1、Redis中保存的 Token 信息
        //         2、當前請求上下文中保存的 Token 信息 
        //         3、Cookie 中保存的 Token 信息(如果未使用Cookie模式則不會清除)
        StpUtil.logout();
        
        // StpUtil.logout() 在未登錄時也是可以調用成功的,
        // 也就是說,無論客戶端有沒有登錄,執行完 StpUtil.logout() 后,都會處于未登錄狀態 
        System.out.println("當前是否處于登錄狀態:" + StpUtil.isLogin());
        
        // 返回給前端 
        return SaResult.ok("退出登錄成功");
    }
    
}

感謝各位的閱讀,以上就是“SpringBoot怎么使用Sa-Token實現登錄認證”的內容了,經過本文的學習后,相信大家對SpringBoot怎么使用Sa-Token實現登錄認證這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

新绛县| 文水县| 新田县| 攀枝花市| 滁州市| 溧阳市| 文安县| 郁南县| 松阳县| 商洛市| 大连市| 双峰县| 呈贡县| 隆尧县| 临安市| 云阳县| 凯里市| 海晏县| 澳门| 外汇| 吉林市| 寿阳县| 肥乡县| 盐山县| 靖远县| 岚皋县| 岳阳市| 绥滨县| 黄大仙区| 吐鲁番市| 西乡县| 即墨市| 邯郸市| 瑞丽市| 龙游县| 格尔木市| 济宁市| 平阳县| 贵定县| 兴和县| 井研县|