位置:首页 > Java > Sa-Token + JWT 集成到 Spring Boot:从登录鉴权到权限控制的完整接入指南

Sa-Token + JWT 集成到 Spring Boot:从登录鉴权到权限控制的完整接入指南

时间:2026-08-24  |  作者:星河游者  |  阅读:0

目录

  1. Sa-Token 和 JWT 分别负责什么
  2. 项目接入后会发生哪些变化
  3. Spring Boot 中的集成步骤
  4. JWT 在这套方案里的角色,以及业务代码怎么用
  5. 前端请求方式与几个必须处理的注意事项
  6. 总结:这套组合适合什么场景
Sa-Token 接入后的安全检查图,展示密码加密、隐藏密码字段和接口拦截范围三项上线前必查内容
上线前的安全检查清单示例代码能跑通不等于能直接上线,这张图把文中的三项关键安全检查拆成可快速核对的清单。
Spring Boot 中 Sa-Token 拦截器的路径放行与角色校验规则图,展示登录接口放行、API 登录校验和管理员删除权限控制
拦截器路径与权限规则这张图集中说明拦截器里的路径匹配逻辑,比直接看代码更容易理解哪些接口会被放行、哪些会被角色限制。
Sa-Token 与 JWT 登录认证链路图,展示登录、签发 token、前端携带 Authorization 访问受保护接口的过程
Sa-Token 与 JWT 认证链路用流程图梳理 Sa-Token 与 JWT 的分工,适合放在解释登录链路的位置。

前言

很多 Spring Boot 项目在补登录态和权限控制时,最容易卡住的不是“能不能接入”,而是 Sa-Token、JWT、拦截器和业务代码各自该放在哪里。这篇文章按“先看改造结果,再走接入步骤,最后处理常见坑”的顺序整理一遍,读完你可以判断自己是否适合用 Sa-Token + JWT,以及一套最小可运行方案该怎么搭。

很多 Spring Boot 项目在补登录态和权限控制时,最容易卡住的不是“能不能接入”,而是 Sa-Token、JWT、拦截器和业务代码各自该放在哪里。这篇文章按“先看改造结果,再走接入步骤,最后处理常见坑”的顺序整理一遍,读完你可以判断自己是否适合用 Sa-Token + JWT,以及一套最小可运行方案该怎么搭。

Sa-Token 和 JWT 分别负责什么

先把两个概念拆开看:

  • Sa-Token:一套 Java 认证授权框架,负责登录认证、权限校验、Token 管理和请求拦截。
  • JWT(JSON Web Token):一种 Token 格式,用来承载用户身份和过期信息。

Sa-Token 默认用随机字符串作为 token;如果你希望令牌是自包含的、便于在分布式场景中传递,可以接入 JWT 作为 token 格式。

┌────────────┬───────────────────────────────────────────────────────┐
│ 功能       │ 说明                                                 │
├────────────┼───────────────────────────────────────────────────────┤
│ 登录认证   │ 用户输入账号密码 → 签发 token → 后续请求带 token 访问 │
├────────────┼───────────────────────────────────────────────────────┤
│ 权限校验   │ 判断用户是否有某个权限或角色                         │
├────────────┼───────────────────────────────────────────────────────┤
│ Token 管理 │ token 的生成、存储、过期、续期                       │
├────────────┼───────────────────────────────────────────────────────┤
│ 拦截器     │ 自动拦截未登录的请求,返回 401                       │
└────────────┴───────────────────────────────────────────────────────┘

从职责上说,Sa-Token 是认证授权框架,JWT 只是其中一种令牌表现形式,这一点在选型和排错时很关键。

项目接入后会发生哪些变化

如果你的接口现在还是完全裸奔状态,引入 Sa-Token 之后,最直观的变化就是访问路径会开始分层:哪些接口允许匿名访问,哪些接口必须登录,哪些接口还要额外校验角色。

改造前

  • POST /api/users → 任何人都能访问,不用登录
  • GET /api/users/1 → 任何人都能查
  • GET /api/users/1/avatar → 任何人都能下载

改造后

  • POST /api/users/login → 登录接口,返回 token
  • GET /api/users/1 → 需要携带 token 才能访问
  • DELETE /api/users/1 → 需要管理员权限才能删

请求头示例:

Authorization: token值

这类改造的重点不在于“把所有接口都拦住”,而在于把匿名接口、登录态接口和管理接口区分清楚。

Spring Boot 中的集成步骤

1. 在 pom.xml 中加入依赖



    cn.dev33
    sa-token-spring-boot3-starter
    1.39.0



    cn.dev33
    sa-token-redis-jackson
    1.39.0



    cn.dev33
    sa-token-jwt
    1.39.0

这里的版本号都保持为 1.39.0。如果你的项目是多模块,建议统一管理版本,避免 starter 和扩展包不一致。

2. 配置 application.yml

Sa-Token 的基础配置决定 token 名称、有效期、并发策略和日志输出;JWT 部分则决定签名密钥和签发者。

############## Sa-Token 配置 ##############
sa-token:
  # token 名称(同时也是 cookie 名称)
  token-name: Authorization
  # token 有效期(秒),7天
  timeout: 604800
  # 是否允许同一账号同时在线
  is-concurrent: true
  # 是否允许同一账号多地登录
  is-share: true
  # token 风格(可选:uuid、simple-uuid、random-32、random-64、random-128)
  token-style: uuid
  # 是否输出操作日志
  is-log: true

# JWT 配置
jwt:
  # 签名密钥,随便写一段字符串
  secret-key: abcdefghijklmnopqrstuvwxyz0123456789
  # token 签发者
  issuer: test-project

原文里这段 YAML 的缩进和注释位置比较混乱,实际使用时要保证格式正确,否则 Spring Boot 无法正常加载。另一个容易忽略的点是:既然你打算用 JWT,就要认真管理 secret-key,不要在生产环境里长期使用随意写死的测试值。

3. 用拦截器统一做登录与角色校验

拦截器是这套方案的核心,它把“哪些路径需要登录、哪些路径跳过校验、哪些请求要检查角色”集中到一个配置类里,而不是散落在每个 Controller 中。

新建文件:src/main/java/com/test/config/SaTokenConfig.java

package com.test.config;

import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpUtil;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class SaTokenConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注册 Sa-Token 拦截器
        registry.addInterceptor(new SaInterceptor(handle -> {
            // 登录校验:需要登录才能访问的路径
            SaRouter.match("/api/**")
                    .notMatch("/api/users/login")
                    .notMatch("/api/users/register")
                    .check(r -> StpUtil.checkLogin());

            // 角色校验示例:只有 admin 才能删除用户
            SaRouter.match("/api/users/**", r -> {
                if (handle.getRequest().getMethod().equals("DELETE")) {
                    StpUtil.checkRole("admin");
                }
            });
        })).addPathPatterns("/**");
    }
}

这段配置体现了两个规则:

  • /api/users/login/api/users/register 放行,因为用户必须先能登录或注册。
  • /api/** 默认要求登录,而对 DELETE 用户接口再追加 admin 角色校验。

这种写法的好处是路径规则一眼能看清,后续扩展 /api/admin/** 之类的后台接口也更方便。

4. 在 Controller 中补齐登录、退出和当前用户接口

接入拦截器之后,业务侧还需要真正提供登录入口,让前端拿到 token,并能查询当前登录用户。

// ========== 登录 ==========
@PostMapping("/login")
public Map login(@RequestBody Map params) {
    String username = params.get("username");
    String password = params.get("password");

    // 校验账号密码
    User user = userService.getUserByUsername(username);
    if (user == null || !user.getPassword().equals(password)) {
        return Map.of("success", false, "message", "用户名或密码错误");
    }

    // 登录,生成 token
    StpUtil.login(user.getId());
    String tokenValue = StpUtil.getTokenValue();

    return Map.of("success", true, "message", "登录成功", "token", tokenValue, "user", user);
}

@GetMapping("/logout")
public Map logout() {
    StpUtil.logout();
    return Map.of("success", true, "message", "退出成功");
}

// 查看当前登录的用户信息
@GetMapping("/me")
public Map me() {
    long userId = StpUtil.getLoginIdAsLong();
    User user = userService.getUserById(userId);
    return Map.of("success", true, "user", user);
}

这里最关键的调用是 StpUtil.login(user.getId())。它完成登录态创建;随后通过 StpUtil.getTokenValue() 取出 token,返回给前端保存并用于后续请求。

JWT 在这套方案里的角色,以及业务代码怎么用

JWT 在登录流程中处于哪一层

JWT 并不替代 Sa-Token,而是作为 token 的一种输出格式参与登录流程。

登录成功
↓
StpUtil.login(用户ID)
↓
Sa-Token 生成 Token
↓
默认:随机字符串格式 → "d8f9a0e1-2b3c-4d5e-..."
JWT:令牌格式 → "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
↓
返回给前端
↓
前端每次请求携带此 token

JWT 的价值在于,token 本身可以包含用户 ID、过期时间等信息,服务端验证时不一定每次都需要查数据库或 Redis,这对分布式部署更友好。不过这并不意味着业务数据完全不查库,例如你要拿完整用户资料时,仍然需要根据登录 ID 查询用户表。

业务代码中的常见用法

1. 获取当前登录用户

// 在 Controller 或 Service 中
long userId = StpUtil.getLoginIdAsLong(); // 获取当前登录的用户ID
User user = userService.getUserById(userId);

2. 校验登录、角色和权限

// 判断是否登录
StpUtil.isLogin()      // true/false
StpUtil.checkLogin()   // 未登录会抛出异常,返回 401

// 判断角色
StpUtil.hasRole("admin")    // true/false
StpUtil.checkRole("admin")  // 没有会抛出异常

// 判断权限
StpUtil.hasPermission("user:delete")  // true/false

3. 给 token 会话写入角色信息

// 在登录成功后给用户分配角色
StpUtil.login(userId);
StpUtil.getTokenSession().set("role", "admin");

实际项目里,角色与权限更常见的做法是从用户、角色、权限表中动态查询,而不是直接把全部授权逻辑硬编码在登录接口里。上面的代码更适合作为快速验证接入是否成功的示例。

前端请求方式与几个必须处理的注意事项

前端如何携带 token 请求接口

前端流程很直接:先调登录接口拿到 token,再把它放进 Authorization 请求头。

# 1. 登录获取 token
curl -X POST http://localhost:8080/api/users/login 
  -H "Content-Type: application/json" 
  -d '{"username":"admin","password":"123456"}'

返回:
{"success":true,"token":"eyJhbGciOiJIUzI1NiIs...","user":{...}}

# 2. 后续请求携带 token
curl http://localhost:8080/api/users/1 
  -H "Authorization: eyJhbGciOiJIUzI1NiIs..."

# 3. 查看个人信息
curl http://localhost:8080/api/users/me 
  -H "Authorization: eyJhbGciOiJIUzI1NiIs..."

无论是前端框架还是移动端调用,本质上都遵循这个过程。真正需要统一的是 token 存储方式、失效后的处理,以及 401 返回时是否自动跳转登录页。

上线前至少要补上的几个安全细节

1. 密码不能明文存储

原示例里直接拿数据库密码和输入密码做字符串比较,这只适合演示流程,不适合生产环境。

// 注册时加密
String encodedPassword = BCrypt.gensalt().encode(password);

// 登录时校验
boolean matches = BCrypt.checkpw(inputPassword, user.getPassword());

2. 返回用户对象时要隐藏密码字段

// 序列化时忽略密码字段
@JsonIgnore
private String password;

3. 拦截器放行与拦截范围要明确

  • /api/users/login → 不拦截(要登录)
  • /api/users/register → 不拦截(要注册)
  • /api/** → 拦截(需要登录)
  • /api/admin/** → 拦截(需要管理员角色)

这几项看起来基础,但大多数“明明接了鉴权却还是不安全”的问题,基本都出在这里:密码明文、用户对象泄漏敏感字段,或者拦截路径写错导致本该保护的接口没有保护。

总结:这套组合适合什么场景

┌───────────────┬────────────────────────────────────────────────┐
│ 组件          │ 在这个项目中的作用                             │
├───────────────┼────────────────────────────────────────────────┤
│ Sa-Token      │ 登录认证、token 管理、权限校验、请求拦截       │
├───────────────┼────────────────────────────────────────────────┤
│ JWT           │ Token 的格式标准(用户信息编码在 token 中)    │
├───────────────┼────────────────────────────────────────────────┤
│ SaInterceptor │ 自动拦截未登录请求,不用每个 Controller 写判断 │
├───────────────┼────────────────────────────────────────────────┤
│ StpUtil       │ 核心工具类,登录/登出/获取用户/校验权限         │
└───────────────┴────────────────────────────────────────────────┘

如果你的 Spring Boot 项目正需要一套上手快、路径拦截清晰、权限校验也能逐步扩展的认证方案,Sa-Token 是比较实用的选择;而 JWT 更像是它在分布式或前后端分离场景下的一种令牌增强。落地时把依赖、配置、拦截器、登录接口和安全细节这几部分接好,基本就能形成完整闭环。

免责声明:文中图文均来自网络,如有侵权请联系删除,心愿游戏发布此文仅为传递信息,不代表心愿游戏认同其观点或证实其描述。

相关文章

更多

精选合集

更多

大家都在玩

热门话题

大家都在看

更多