一篇搞定!SpringBoot 搭建超安全 Sa-Token 登錄鑒權(quán)系統(tǒng)
在現(xiàn)代 Java Web 項目中,登錄認證與權(quán)限管理是不可或缺的基礎(chǔ)功能。傳統(tǒng)的框架如 Spring Security 和 Shiro 生態(tài)成熟,但初學者和中小項目開發(fā)者常常覺得配置繁瑣、學習成本高。
Sa-Token 的出現(xiàn)正好填補了這一空白:它是一款輕量級 Java 權(quán)限認證框架,既能滿足登錄認證、權(quán)限校驗、單點登錄(SSO)、OAuth2.0、分布式 Session 和微服務(wù)網(wǎng)關(guān)鑒權(quán)等需求,又極大降低了上手門檻。本文將帶你從零開始,在 Spring Boot 環(huán)境下快速搭建一個完整的登錄鑒權(quán)系統(tǒng),并詳細講解原理和最佳實踐。
官網(wǎng)地址:https://sa-token.cc/
Sa-Token 是什么?
Sa-Token 是一個輕量級、功能全面的 Java 權(quán)限框架,核心功能包括:
- 登錄認證(單賬號或多賬號體系)
- 權(quán)限校驗(角色權(quán)限、功能權(quán)限)
- 單點登錄(SSO)
- OAuth2.0 授權(quán)
- 分布式 Session 管理
- 微服務(wù)網(wǎng)關(guān)鑒權(quán)
簡單來說,Sa-Token 能解決大多數(shù)項目中遇到的權(quán)限相關(guān)問題,功能齊全且易上手,是國內(nèi)很多互聯(lián)網(wǎng)公司正在使用的技術(shù)。
為什么選擇 Sa-Token?
相比 Shiro、Spring Security,Sa-Token 有幾個顯著優(yōu)勢:
- 文檔友好 官方文檔全中文,內(nèi)容詳盡,查閱和理解門檻低。
- 生態(tài)穩(wěn)定 雖然是新晉框架,但在國內(nèi)開源社區(qū)已經(jīng)形成完整生態(tài),曾多次位列 Gitee 最受歡迎開源項目。
- 極簡易用 使用 Sa-Token,僅需幾行代碼即可實現(xiàn)完整登錄鑒權(quán)功能,而同樣功能使用 Spring Security 可能需要上百行配置。
總結(jié):如果你希望快速搭建安全、功能完善的權(quán)限系統(tǒng),同時避免繁瑣配置,Sa-Token 是一個非常理想的選擇。
Spring Boot 集成 Sa-Token
引入依賴
以 Spring Boot 2.7.3 為例:
<!-- pom.xml -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.36.0</version>
</dependency>如果是 Spring Boot 3.x,只需將依賴改為:
<artifactId>sa-token-spring-boot3-starter</artifactId>配置文件(application.yml)
Sa-Token 提供默認配置,可選自定義:
sa-token:
token-name: sa-token # Cookie 名稱
token-style: uuid # Token 風格,可選 uuid|simple-uuid|random-32|random-64|random-128|tik主啟動類示例
package com.icoderoad.satoken;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import cn.dev33.satoken.stp.SaManager;
@SpringBootApplication
public class SaTokenApplication {
public static void main(String[] args) {
SpringApplication.run(SaTokenApplication.class, args);
// 打印 Sa-Token 配置信息
System.out.println(SaManager.getConfig());
}
}如果沒有自定義配置,SaManager.getConfig() 將打印默認配置。
編寫測試 Controller
package com.icoderoad.satoken.controller;
import org.springframework.web.bind.annotation.*;
import cn.dev33.satoken.stp.StpUtil;
@RestController
@RequestMapping("/auth")
public class AuthController {
// 登錄接口(測試用,用戶名密碼寫死)
@PostMapping("/login")
public String doLogin(@RequestParam String username, @RequestParam String password) {
if("admin".equals(username) && "123456".equals(password)) {
StpUtil.login(10001); // 登錄用戶,唯一 ID 10001
return "登錄成功";
}
return "用戶名或密碼錯誤";
}
// 判斷登錄狀態(tài)
@GetMapping("/isLogin")
public boolean isLogin() {
return StpUtil.isLogin();
}
// 注銷
@PostMapping("/logout")
public String logout() {
StpUtil.logout();
return "注銷成功";
}
}原理解析:StpUtil.login() 內(nèi)部完成了:
- 檢查賬號是否已登錄
- 生成唯一 Token 并創(chuàng)建 Session
- 記錄 Token 活躍時間
- 通知全局監(jiān)聽器
- 將 Token 注入 Cookie
瀏覽器會自動攜帶 Cookie,后續(xù)請求無需手動傳遞 Token。
權(quán)限校驗
告訴 Sa-Token 用戶權(quán)限
實現(xiàn) StpInterface 接口:
package com.icoderoad.satoken.config;
import cn.dev33.satoken.stp.StpInterface;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class StpInterfaceImpl implements StpInterface {
@Override
public List<String> getPermissionList(Object loginId, String loginType) {
return List.of("user.add","user.update","user.get","user.delete");
}
@Override
public List<String> getRoleList(Object loginId, String loginType) {
return List.of("admin","manager");
}
}校驗權(quán)限
@GetMapping("/user/list")
public String getUserList() {
StpUtil.checkPermission("user-list"); // 如果沒有權(quán)限,會拋出異常
return "用戶列表信息";
}添加全局異常處理后,前端報錯信息會更友好。
注解鑒權(quán)
Sa-Token 提供兩種注解鑒權(quán)方式:
- 基于攔截器:適合 Controller 層,默認關(guān)閉,需要手動注冊全局攔截器。
- 基于 AOP:可應用于任意層,侵入性低,注意 AOP 與攔截器不可同時啟用。
示例:
@SaCheckPermission("user-add")
@PostMapping("/user/add")
public String addUser() {
return "新增用戶成功";
}集成 Redis(分布式環(huán)境)
內(nèi)存存儲有兩個問題:
- 重啟后數(shù)據(jù)丟失
- 多實例共享困難
解決方案:使用 Redis 作為 Token 存儲。
spring:
redis:
host: localhost
port: 6379
sa-token:
is-read-cookie: true
is-read-header: true登錄后,Sa-Token 會自動將 Token 和 Session 信息寫入 Redis,支持集群環(huán)境。
前后端分離應用
登錄成功后,手動返回 Token 信息:
@PostMapping("/login")
public Map<String,Object> login(@RequestParam String username,@RequestParam String password){
StpUtil.login(10001);
return StpUtil.getTokenInfo();
}前端將 Token 存儲在本地,每次請求通過 Header 攜帶:
Header: tokenName=tokenValueSa-Token 自動解析請求頭,完成身份驗證。
前后端分離場景下 Token 使用
前后端分離時無法依賴 Cookie,需要手動管理 Token:
- 登錄成功返回 Token 信息
- 前端存儲 Token(localStorage 或 Vue/React 狀態(tài)管理)
- 請求接口時將 Token 放入 Header
// login.js
async function login() {
const username = document.getElementById("username").value;
const password = document.getElementById("password").value;
const response = await fetch("/auth/login", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: `username=${username}&password=${password}`
});
const data = await response.json();
if(data.tokenValue){
localStorage.setItem(data.tokenName, data.tokenValue);
alert("登錄成功");
} else {
alert(data.msg);
}
}
// 請求接口示例
async function getUserList() {
const tokenName = "sa-token";
const tokenValue = localStorage.getItem(tokenName);
const response = await fetch("/auth/user/list", {
method: "GET",
headers: { [tokenName]: tokenValue }
});
const text = await response.text();
alert(text);
}前端頁面(Thymeleaf + Bootstrap 示例)
<!-- templates/login.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Sa-Token 登錄示例</title>
<link rel="stylesheet" >
</head>
<body class="bg-light">
<div class="container mt-5">
<h2 class="mb-4">登錄</h2>
<div class="mb-3">
<input type="text" class="form-control" id="username" placeholder="用戶名">
</div>
<div class="mb-3">
<input type="password" class="form-control" id="password" placeholder="密碼">
</div>
<button class="btn btn-primary" onclick="login()">登錄</button>
<button class="btn btn-secondary" onclick="getUserList()">獲取用戶列表</button>
</div>
<script>
async function login() {
const username = document.getElementById("username").value;
const password = document.getElementById("password").value;
const params = new URLSearchParams({username, password});
const response = await fetch("/auth/login", {method:"POST", body: params});
const data = await response.json();
if(data.tokenValue){
localStorage.setItem(data.tokenName, data.tokenValue);
alert("登錄成功");
} else {
alert(data.msg);
}
}
async function getUserList() {
const tokenName = "sa-token";
const tokenValue = localStorage.getItem(tokenName);
const response = await fetch("/auth/user/list", {
headers: { [tokenName]: tokenValue }
});
const text = await response.text();
alert(text);
}
</script>
</body>
</html>總結(jié)與擴展
通過本文示例,我們完成了:
- Spring Boot 項目快速集成 Sa-Token
- 用戶登錄、注銷、狀態(tài)校驗
- 權(quán)限與角色管理
- 注解鑒權(quán)與 API 鑒權(quán)
- Redis 分布式存儲支持
- 前后端分離項目 Token 使用
Sa-Token 還支持記住我、同端互斥、二級認證、賬號封禁、單點登錄、OAuth2 等功能,接入簡單、可擴展性強。相比 Spring Security 和 Shiro,它在國內(nèi)項目中具有更快的上手體驗和靈活性。如果你想快速搭建安全、高效的權(quán)限系統(tǒng),Sa-Token 是一個值得嘗試的方案。





























