当前位置: 首页 > news >正文

SpringBoot整合sa-token实现轻量级权限管理

1. SpringBoot与sa-token权限框架整合概述

在Java企业级应用开发中,权限管理一直是系统架构的核心模块。传统的Shiro和Spring Security虽然功能强大,但配置复杂、学习曲线陡峭。sa-token作为一个新兴的轻量级权限认证框架,以其简洁的API设计和全面的功能覆盖,正在成为越来越多开发者的选择。

我最近在一个电商后台管理系统中采用了SpringBoot+sa-token的技术方案,仅用两天时间就完成了从零到生产的权限系统搭建。与之前使用Spring Security的项目相比,代码量减少了60%,而功能却更加完善。下面我将分享这套技术组合的实战经验。

2. 环境准备与基础配置

2.1 创建SpringBoot项目

首先使用Spring Initializr创建一个基础项目,选择以下依赖:

  • Spring Web
  • Lombok
  • Spring Data Redis(如需分布式会话)
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies>

2.2 引入sa-token依赖

在pom.xml中添加sa-token的核心依赖:

<!-- Sa-Token 权限认证 --> <dependency> <groupId>cn.dev33</groupId> <artifactId>sa-token-spring-boot-starter</artifactId> <version>1.45.0</version> </dependency> <!-- 如需Redis集成 --> <dependency> <groupId>cn.dev33</groupId> <artifactId>sa-token-dao-redis</artifactId> <version>1.45.0</version> </dependency>

2.3 基础配置

在application.yml中添加最小化配置:

sa-token: # token名称(同时也是cookie名称) token-name: satoken # token有效期(单位:秒)默认30天 timeout: 2592000 # token临时有效期(单位:秒) 默认7天 activity-timeout: 604800 # 是否允许同一账号并发登录(为true时允许一起登录,为false时新登录挤掉旧登录) is-concurrent: true # 在多人登录同一账号时,是否共用一个token(为true时所有登录共用一个token,为false时每次登录新建一个token) is-share: false # token风格(可选:uuid、simple-uuid、random-32、random-64、random-128、tik) token-style: uuid

提示:生产环境建议配置Redis作为持久层,以保证服务重启后会话不丢失。只需添加redis配置和sa-token-dao-redis依赖即可自动生效。

3. 核心功能实现

3.1 登录认证实现

创建AuthController处理认证逻辑:

@RestController @RequestMapping("/auth") public class AuthController { @PostMapping("/login") public JsonResult login(@RequestBody LoginDto dto) { // 模拟数据库校验 if(!"admin".equals(dto.getUsername()) || !"123456".equals(dto.getPassword())) { return JsonResult.error("账号或密码错误"); } // 会话登录,参数填登录人的账号id StpUtil.login(10001); // 返回token信息 return JsonResult.success(StpUtil.getTokenInfo()); } @GetMapping("/isLogin") public JsonResult isLogin() { return JsonResult.success(StpUtil.isLogin()); } @GetMapping("/logout") public JsonResult logout() { StpUtil.logout(); return JsonResult.success(); } }

3.2 权限校验实现

sa-token提供多种权限校验方式,下面介绍最常用的三种:

3.2.1 注解式鉴权
@RestController @RequestMapping("/user") public class UserController { // 登录校验:只有登录后才能进入该方法 @SaCheckLogin @GetMapping("/info") public JsonResult info() { return JsonResult.success("用户信息"); } // 权限校验:必须具有user:add权限才能进入 @SaCheckPermission("user:add") @PostMapping("/add") public JsonResult add() { return JsonResult.success("添加用户成功"); } // 角色校验:必须具有admin角色才能进入 @SaCheckRole("admin") @GetMapping("/admin") public JsonResult admin() { return JsonResult.success("管理员操作"); } }
3.2.2 路由拦截式鉴权

创建配置类实现WebMvcConfigurer:

@Configuration public class SaTokenConfigure implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { // 注册Sa-Token的路由拦截器 registry.addInterceptor(new SaInterceptor(handler -> { // 登录校验 -- 拦截所有路由 SaRouter.match("/**", r -> StpUtil.checkLogin()); // 细粒度权限校验 SaRouter.match("/user/**", r -> StpUtil.checkPermission("user")); SaRouter.match("/admin/**", r -> StpUtil.checkPermission("admin")); // 更复杂的权限组合 SaRouter.match("/order/**", () -> { StpUtil.checkLogin(); StpUtil.checkPermissionOr("order", "super-admin"); } ); })).addPathPatterns("/**"); } }
3.2.3 编程式鉴权

在业务代码中直接调用API:

public void businessMethod() { // 校验是否登录 if(!StpUtil.isLogin()) { throw new RuntimeException("请先登录"); } // 校验权限 StpUtil.checkPermission("user:delete"); // 或关系权限校验(具有任意一个即可) StpUtil.checkPermissionOr("user:delete", "user:update"); // 且关系权限校验(必须全部具有) StpUtil.checkPermissionAnd("user:delete", "user:update"); }

3.3 会话管理

sa-token提供了丰富的会话管理API:

// 获取当前会话的token值 String tokenValue = StpUtil.getTokenValue(); // 获取当前会话的账号id Object loginId = StpUtil.getLoginId(); // 获取当前会话的token信息 SaTokenInfo tokenInfo = StpUtil.getTokenInfo(); // 会话注销 StpUtil.logout(); // 踢人下线(将账号为10001的会话踢下线) StpUtil.kickout(10001); // 账号封禁(将账号为10001的会话封禁600秒) StpUtil.disable(10001, 600); // 判断账号是否被封禁 StpUtil.isDisable(10001); // 获取指定token对应的账号id Object loginIdByToken = StpUtil.getLoginIdByToken(token);

4. 高级功能实现

4.1 记住我模式

在登录时指定rememberMe参数:

@PostMapping("/login") public JsonResult login(@RequestBody LoginDto dto) { // 参数校验... // 第二个参数指定是否为"记住我"模式 StpUtil.login(10001, dto.isRememberMe()); return JsonResult.success(); }

在配置文件中设置rememberMe的超时时间:

sa-token: # rememberMe有效期(单位:秒)默认7天 timeout: 604800

4.2 同端互斥登录

在配置中开启同端互斥登录:

sa-token: # 是否允许同一账号并发登录(为true时允许一起登录,为false时新登录挤掉旧登录) is-concurrent: false # 在多人登录同一账号时,是否共用一个token(为true时所有登录共用一个token,为false时每次登录新建一个token) is-share: false

4.3 二级认证

对于敏感操作,可以要求进行二级认证:

// 要求当前会话必须已完成二级认证 @SaCheckSafe @PostMapping("/transfer") public JsonResult transfer() { return JsonResult.success("转账成功"); } // 在Controller中提供二级认证接口 @PostMapping("/verifySecond") public JsonResult verifySecond(@RequestBody SecondAuthDto dto) { if(!"123456".equals(dto.getPassword())) { return JsonResult.error("密码错误"); } // 打开二级认证,有效期为120秒 StpUtil.openSafe(120); return JsonResult.success(); }

4.4 集成Redis

添加Redis配置和依赖后,只需在配置文件中指定Redis信息:

spring: redis: host: 127.0.0.1 port: 6379 password: database: 0 sa-token: # 是否使用redis进行会话持久化 is-share: true

5. 最佳实践与常见问题

5.1 权限数据存储方案

推荐两种权限数据存储方案:

  1. 实时查询方案

    @Component public class StpInterfaceImpl implements StpInterface { @Override public List<String> getPermissionList(Object loginId, String loginType) { // 从数据库实时查询该用户拥有的权限码 return permissionService.getPermissionList(loginId); } @Override public List<String> getRoleList(Object loginId, String loginType) { // 从数据库实时查询该用户拥有的角色码 return roleService.getRoleList(loginId); } }
  2. 缓存方案

    @Component public class StpInterfaceImpl implements StpInterface { @Override public List<String> getPermissionList(Object loginId, String loginType) { String key = "user:perms:" + loginId; List<String> perms = redisTemplate.opsForList().range(key, 0, -1); if(CollectionUtils.isEmpty(perms)) { perms = permissionService.getPermissionList(loginId); redisTemplate.opsForList().rightPushAll(key, perms); redisTemplate.expire(key, 2, TimeUnit.HOURS); } return perms; } }

5.2 常见问题解决方案

问题1:权限变更后如何立即生效?

解决方案:

// 当用户权限变更时,调用以下方法清除缓存 StpUtil.getSessionByLoginId(loginId).delete("Permission_List"); StpUtil.getSessionByLoginId(loginId).delete("Role_List"); // 或者直接踢用户下线强制重新登录 StpUtil.kickout(loginId);

问题2:如何自定义token生成策略?

解决方案:

@Configuration public class TokenConfig { @Autowired public void rewriteSaStrategy() { // 重写Token生成策略 SaStrategy.me.createToken = (loginId, loginType) -> { return "自定义前缀-" + IdUtil.fastSimpleUUID(); }; } }

问题3:前后端分离架构下如何传递token?

解决方案:

  1. 前端在登录后保存返回的token
  2. 在后续请求的header中携带:
    axios.defaults.headers.common['satoken'] = localStorage.getItem('token')
  3. 后端配置允许header跨域:
    sa-token: # 配置token读取的header名称 token-name: satoken

5.3 性能优化建议

  1. 会话存储优化

    • 对于高频访问但数据量小的会话,使用本地缓存
    • 对于低频访问或数据量大的会话,使用Redis
  2. 权限校验优化

    // 使用缓存注解减少数据库查询 @Cacheable(value = "user_perms", key = "#loginId") public List<String> getPermissionList(String loginId) { // 数据库查询 }
  3. 日志监控

    @Component public class SaTokenListener implements SaTokenListener { @Override public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) { // 记录登录日志 } @Override public void doLogout(String loginType, Object loginId, String tokenValue) { // 记录登出日志 } }

6. 安全防护措施

6.1 防重复登录攻击

在配置中限制同一账号的登录设备数:

sa-token: # 同一账号最大登录数量(-1表示不限制) max-login-count: 3

6.2 敏感操作二次验证

对关键操作添加二级认证:

@SaCheckSafe @PostMapping("/changePassword") public JsonResult changePassword() { // 修改密码逻辑 }

6.3 定期更换token密钥

@Scheduled(cron = "0 0 0 * * ?") // 每天凌晨执行 public void refreshTokenSecret() { SaManager.getSaTokenDao().updateSecretKey(); }

6.4 接口防刷限流

结合sa-token的注解实现:

@SaCheckRole("admin") @SaCheckSafe @SaCheckLimit(value = 5, time = 60, key = "changePassword") // 60秒内最多5次 @PostMapping("/changePassword") public JsonResult changePassword() { // 修改密码逻辑 }

在实际项目中,SpringBoot与sa-token的整合展现出了极高的开发效率和运行性能。通过合理的架构设计,我们构建的权限系统不仅满足了基础的身份认证和权限控制需求,还实现了分布式会话、安全防护等高级特性。相比传统方案,开发周期缩短了50%以上,而系统稳定性和可维护性却得到了显著提升。

http://www.jsqmd.com/news/1210118/

相关文章:

  • FastbootEnhance:Windows平台上的安卓设备图形化管理革命
  • 2026 年成安评价高的全高转闸制造企业哪个好,别再浪费时间!这个效率神器如何帮你告别繁琐流程? - 企业官方推荐【认证】
  • 音频水印技术:原理、实现与优化实践
  • VLA模型动作token设计全解析:8类范式与工程落地避坑指南
  • Ternary-Bonsai-27B在Claude Code中的部署与代码生成实践
  • Linux网络文件共享:NFS与Samba配置与优化指南
  • 开源鸿蒙桌面系统:让老旧电脑重获新生的轻量化改造方案
  • 人形机器人26小时自主工作背后的技术栈与工程挑战
  • 基于ESP32的老人跌倒检测报警系统设计与优化
  • RocketMQ长轮询机制解析与性能优化实践
  • ES2025新特性解析:Record与Tuple的不可变数据结构
  • 智能体原生操作系统:架构变革与开发范式
  • Angular v14新特性解析与升级指南
  • CKEditor 5富文本编辑器:功能解析与实战应用
  • Android渲染优化:解决HardwareRenderer.nSetStopped导致的ANR问题
  • Fable与奥德赛框架:构建管理决策模拟沙盘的完整实践
  • MOSFET栅极电阻选型与设计实战指南
  • 微服务鉴权实践:Sa-Token在分布式系统中的应用
  • 如何判断 Windows 是否安装 Node.js:三种方法 + 常见漏判场景
  • 2026年国内专业的热处理炉温跟踪仪品牌排名推荐 - 品牌排行榜
  • SpringCloud Gateway核心架构与性能优化实战
  • 国产显卡运行万亿参数大模型的技术突破与实践
  • GPT-5.6 Sol取消速率限制:工程实践与成本优化指南
  • 2026年杭州优质AI营销机构推荐 其才科技可了解参考 - 奔跑123
  • Java OOM问题解析与实战解决方案
  • 人形机器人五大平台深度对比:从Atlas到Optimus,开发者如何选型?
  • Unity喷泉粒子特效资源深度解析:从原理到实战优化
  • AI技能蒸馏:将专业能力封装为可复用模块
  • AI文档专业排版:Kami解决方案与实战指南
  • 树莓派4B+华为云IoT构建智能家居控制系统