下面给你一套**可直接复制运行、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. 测试流程(直接用)
- 启动 Redis
- 启动项目
- 访问:
- 登录:
POST http://localhost:8080/user/login?username=admin&password=123456 - 拿到 token 放在 Header:
satoken:xxx - 测试:
/user/info登录即可/user/admin需要 admin 角色/user/delete需要权限码