第二十五节 SpringBoot3 集成 Sa-Token

亮子 | 2026-04-08 10:54:01 | 23 | 0 | 0 | 0

下面给你一套**可直接复制运行、SpringBoot3 + Sa-Token 完整权限骨架**,包含:
登录、鉴权、Redis、全局异常、注解权限、自定义权限规则。


1. 项目结构(标准三层)

com.example
├── SaTokenApplication.java        // 启动类
├── config
│   └── SaTokenConfig.java         // 拦截器、跨域
├── controller
│   └── UserController.java        // 登录 + 测试接口
├── entity
│   └── User.java                  // 用户实体
├── service
│   ├── UserService.java
│   └── impl
│       └── UserServiceImpl.java
├── core
│   └── StpInterfaceImpl.java      // 权限/角色来源
└── exception
    └── GlobalExceptionHandler.java // 全局异常

2. pom.xml(SpringBoot3 + Sa-Token + Redis)

<dependencies>
    <!-- SpringBoot Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Sa-Token 启动器 -->
    <dependency>
        <groupId>cn.dev33</groupId>
        <artifactId>sa-token-spring-boot3-starter</artifactId>
        <version>1.44.0</version>
    </dependency>

    <!-- Sa-Token Redis 集成 -->
    <dependency>
        <groupId>cn.dev33</groupId>
        <artifactId>sa-token-redis</artifactId>
        <version>1.44.0</version>
    </dependency>

    <!-- Redis -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

    <!-- lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

3. application.yml

server:
  port: 8080

spring:
  redis:
    host: 127.0.0.1
    port: 6379
    database: 0
    timeout: 10s

# Sa-Token 配置
sa-token:
  token-name: satoken           # token 名称(同时是 cookie 名称)
  timeout: 2592000              # token 有效期 30天
  active-timeout: -1            # 不限制活跃超时
  is-concurrent: true           # 允许同账号多端登录
  is-share: true                # token 共享
  token-style: uuid             # token 风格
  is-log: true                  # 打印日志
  is-read-header: true          # 从header读取token
  is-read-cookie: true

4. 启动类

@SpringBootApplication
public class SaTokenApplication {
    public static void main(String[] args) {
        SpringApplication.run(SaTokenApplication.class, args);
    }
}

5. SaToken 配置(拦截器 + 跨域)

@Configuration
public class SaTokenConfig implements WebMvcConfigurer {

    /**
     * 注册 Sa-Token 拦截器,定义详细认证规则
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new SaInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/user/login");
    }

    /**
     * 跨域配置
     */
    @Bean
    public CorsFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOriginPattern("*");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }
}

6. 全局异常处理

@RestControllerAdvice
public class GlobalExceptionHandler {

    // 未登录
    @ExceptionHandler(NotLoginException.class)
    public SaResult handlerNotLogin(NotLoginException e) {
        return SaResult.error(401, "未登录:" + e.getMessage());
    }

    // 无角色
    @ExceptionHandler(NotRoleException.class)
    public SaResult handlerNotRole(NotRoleException e) {
        return SaResult.error(403, "无角色权限:" + e.getMessage());
    }

    // 无权限码
    @ExceptionHandler(NotPermissionException.class)
    public SaResult handlerNotPermission(NotPermissionException e) {
        return SaResult.error(403, "无功能权限:" + e.getMessage());
    }

    // 其他异常
    @ExceptionHandler(Exception.class)
    public SaResult handlerException(Exception e) {
        e.printStackTrace();
        return SaResult.error("系统异常:" + e.getMessage());
    }
}

7. 核心:权限/角色来源(从数据库查)

@Component
public class StpInterfaceImpl implements StpInterface {

    /**
     * 返回账号拥有的权限码
     */
    @Override
    public List<String> getPermissionList(Object loginId, String loginType) {
        // 这里模拟从数据库查询
        if ("1001".equals(loginId.toString())) {
            return Arrays.asList("user:add", "user:delete", "user:update", "user:list");
        }
        return Collections.emptyList();
    }

    /**
     * 返回账号拥有的角色
     */
    @Override
    public List<String> getRoleList(Object loginId, String loginType) {
        if ("1001".equals(loginId.toString())) {
            return Arrays.asList("admin");
        }
        return Collections.emptyList();
    }
}

8. Controller(登录 + 权限测试)

@RestController
@RequestMapping("/user")
public class UserController {

    /**
     * 登录接口
     */
    @PostMapping("/login")
    public SaResult login(String username, String password) {
        // 模拟数据库校验
        if (!"admin".equals(username) || !"123456".equals(password)) {
            return SaResult.error("账号或密码错误");
        }
        // 登录
        StpUtil.login(1001);
        return SaResult.ok("登录成功")
                .set("token", StpUtil.getTokenValue())
                .set("loginId", StpUtil.getLoginId());
    }

    /**
     * 必须登录才能访问
     */
    @SaCheckLogin
    @GetMapping("/info")
    public SaResult info() {
        return SaResult.ok("当前用户:" + StpUtil.getLoginId());
    }

    /**
     * 必须有 admin 角色
     */
    @SaCheckRole("admin")
    @GetMapping("/admin")
    public SaResult admin() {
        return SaResult.ok("管理员页面");
    }

    /**
     * 必须有 user:delete 权限
     */
    @SaCheckPermission("user:delete")
    @GetMapping("/delete")
    public SaResult delete() {
        return SaResult.ok("执行删除操作");
    }

    /**
     * 登出
     */
    @SaCheckLogin
    @GetMapping("/logout")
    public SaResult logout() {
        StpUtil.logout();
        return SaResult.ok("登出成功");
    }
}

9. 测试流程(直接用)

  1. 启动 Redis
  2. 启动项目
  3. 访问:
  • 登录:POST http://localhost:8080/user/login?username=admin&password=123456
  • 拿到 token 放在 Header:satoken:xxx
  • 测试:
    • /user/info 登录即可
    • /user/admin 需要 admin 角色
    • /user/delete 需要权限码