Sa-Token替代Shiro:Java权限认证框架的现代化实践
1. 为什么选择Sa-Token替代Shiro作为一名在Java权限认证领域摸爬滚打多年的开发者我见证了从早期手动实现Session管理到Shiro、Spring Security等框架的演进过程。最近两年Sa-Token这个后起之秀逐渐进入我的视野经过多个生产项目的实战验证后我决定全面转向Sa-Token。这个决定并非一时冲动而是基于以下几个核心痛点的解决1.1 API设计理念的差异Shiro虽然功能强大但其API设计存在明显的历史包袱。比如实现一个简单的登录功能需要配置Realm、编写CredentialsMatcher还要处理各种异常情况。而Sa-Token的API设计完全遵循约定优于配置的原则// Shiro登录需要至少20行代码 Subject subject SecurityUtils.getSubject(); UsernamePasswordToken token new UsernamePasswordToken(username, password); try { subject.login(token); return 登录成功; } catch (AuthenticationException e) { return 登录失败; } // Sa-Token只需1行 StpUtil.login(userId);1.2 会话管理的复杂度Shiro的Session管理存在几个明显问题默认使用Servlet容器Session集群环境下需要额外配置Session属性操作需要强转类型分布式Session需要集成第三方缓存Sa-Token的会话管理则更加现代化// 存储任意类型值自动序列化 StpUtil.getSession().set(userInfo, user); // 分布式环境下默认支持Redis // 只需配置redis连接即可开箱即用1.3 注解鉴权的优雅程度权限校验是权限框架的核心功能我们对比下两者的实现方式// Shiro需要定义繁琐的权限字符串 RequiresPermissions(user:add) public void addUser(User user) {...} // Sa-Token支持更灵活的表达式 SaCheckPermission(user:add || admin:full) public void addUser(User user) {...}1.4 前后端分离的支持现代应用普遍采用前后端分离架构Shiro在这方面的支持明显不足默认依赖Cookie机制Token处理需要自行扩展跨域问题需要额外处理Sa-Token原生支持各种认证方式# 支持Cookie、Header、URL参数等多种token传递方式 sa-token.token-styleuuid sa-token.is-read-headertrue sa-token.is-read-cookiefalse2. Sa-Token核心功能深度解析2.1 登录认证机制剖析Sa-Token的登录认证设计极简但功能完备。其核心原理是通过StpUtil这个静态工具类提供各种认证操作。底层架构上它采用了Token-Session模型Token生成策略支持UUID、简单UUID、32位随机字符串、64位随机字符串、JWT等多种风格会话存储结构{ tokenValue: xxxx, loginId: 10001, loginType: web, device: pc, timeout: 1800 }多端登录控制// 允许同一账号在三台设备同时登录 StpUtil.login(10001, PC); StpUtil.login(10001, APP); StpUtil.login(10001, PAD); // 设置同端互斥登录如微信 SaManager.getConfig().setConcurrentSame(true);2.2 权限认证实现原理Sa-Token的权限系统采用RBAC模型但实现更加灵活。其核心设计特点包括权限通配符支持// 支持*号通配符 SaCheckPermission(user:*) public void userOperation() {...}角色权限分离// 角色验证 SaCheckRole(admin) // 权限验证 SaCheckPermission(user:delete) // 且关系验证 SaCheckRole(admin).and(SaCheckPermission(user:delete))动态权限控制// 动态添加权限 StpUtil.getRoleList(); // 获取角色列表 StpUtil.getPermissionList(); // 获取权限列表 // 可结合数据库实现动态权限 ListString permissionList permissionService.getByUserId(StpUtil.getLoginId()); StpUtil.getSession().set(permission, permissionList);2.3 分布式会话解决方案Sa-Token的分布式会话设计是其最大亮点之一。默认情况下它会自动检测项目环境单机模式使用内存Map存储会话数据Redis模式检测到Redis配置后自动切换# Redis配置示例 sa-token.jwt-secret-keyxxxx sa-token.is-share-redistrue sa-token.redis-host127.0.0.1 sa-token.redis-port6379自定义存储实现SaSessionDao接口即可扩展Component public class MySessionDao implements SaSessionDao { Override public SaSession getSession(String sessionId, boolean isCreate) { // 自定义实现 } }3. 实战从Shiro迁移到Sa-Token3.1 迁移准备工作依赖调整!-- 移除Shiro依赖 -- !-- dependency groupIdorg.apache.shiro/groupId artifactIdshiro-spring/artifactId /dependency -- !-- 添加Sa-Token -- dependency groupIdcn.dev33/groupId artifactIdsa-token-spring-boot-starter/artifactId version1.45.0/version /dependency配置转换Shiro配置项Sa-Token对应方案shiroFilterSaInterceptorRealm无需实现直接操作StpUtilSessionManager自动管理可配置sa-token.timeout代码改造重点替换所有Subject相关操作重写权限注解调整会话存储方式3.2 核心功能迁移示例登录认证迁移// Shiro方式 Subject subject SecurityUtils.getSubject(); UsernamePasswordToken token new UsernamePasswordToken(username, password); subject.login(token); // Sa-Token方式 // 1. 验证账号密码需自行实现 User user userService.checkPassword(username, password); // 2. 登录 StpUtil.login(user.getId());权限校验迁移// Shiro方式 RequiresPermissions(user:add) public void addUser() {...} // Sa-Token方式 SaCheckPermission(user:add) public void addUser() {...}会话管理迁移// Shiro方式 Session session SecurityUtils.getSubject().getSession(); session.setAttribute(key, value); // Sa-Token方式 StpUtil.getSession().set(key, value);3.3 高级功能对等实现RememberMe功能// Shiro需要复杂配置 token.setRememberMe(true); // Sa-Token一行解决 StpUtil.login(10001, true);动态权限控制// Shiro需要自定义Realm protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { // 查询数据库获取权限 } // Sa-Token动态权限 SaCheckPermission(user:add) public void addUser() { // 方法内可再次验证 if(!StpUtil.hasPermission(user:add)) { throw new ApiException(无权限); } }4. 生产环境最佳实践4.1 性能优化方案Redis配置优化# 使用连接池 sa-token.redis-pool.max-active200 sa-token.redis-pool.max-wait1000 # 选择高效序列化 sa-token.redis-serializationjackson会话存储策略// 对于不常变化的权限数据 SaCheckPermission(user:add) public void addUser() { // 方法内缓存权限验证结果 if(CacheUtil.get(perm_cache_ StpUtil.getLoginId()) null) { ListString perms loadFromDB(); CacheUtil.set(perm_cache_ StpUtil.getLoginId(), perms, 3600); } }Token过期策略# 灵活配置过期时间 sa-token.timeout1800 sa-token.activity-timeout300 sa-token.is-concurrenttrue4.2 安全加固措施防重复登录// 启用同端互斥登录 SaManager.getConfig().setConcurrentSame(true); // 强制下线前一个登录 StpUtil.replaced(10001, PC);敏感操作二次验证// 开启二级认证 SaCheckSafe(password) public void changePassword() {...} // 验证二级密码 if(!StpUtil.checkSafe(password, inputPassword)) { throw new ApiException(二次验证失败); }防CSRF攻击# 启用Token签名验证 sa-token.is-signtrue sa-token.sign-keyyour_sign_key4.3 监控与运维会话查询接口// 获取在线用户列表 ListString onlineUsers StpUtil.searchSessionId(, 0, 10); // 获取指定用户的会话 SaSession session StpUtil.getSessionByLoginId(10001);踢人下线管理// 根据账号踢人 StpUtil.kickout(10001); // 根据Token踢人 StpUtil.kickoutByTokenValue(xxxx);登录日志记录// 注册全局侦听器 Component public class MySaListener implements SaListener { Override public void doListen(String event, Object data) { if(SaEvent.LOGIN.equals(event)) { log.info(用户{}登录, data); } } }5. 常见问题与解决方案5.1 迁移过程中的典型问题会话不兼容问题问题描述从Shiro迁移后原有用户需要重新登录 解决方案实现会话转换器将Shiro Session数据迁移到Sa-Tokenpublic void transferSession(Subject subject) { Session shiroSession subject.getSession(); SaSession saSession StpUtil.getSession(); shiroSession.getAttributeKeys().forEach(key - { saSession.set(key, shiroSession.getAttribute(key)); }); }注解不生效问题问题描述SaCheckPermission注解无效 解决方案检查是否添加了拦截器配置Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new SaInterceptor()).addPathPatterns(/**); }Redis序列化异常问题描述存储复杂对象时出现序列化错误 解决方案使用Jackson替代默认序列化sa-token.redis-serializationjackson5.2 性能问题排查Redis连接池耗尽// 监控Redis连接状态 Scheduled(fixedRate 60000) public void monitorRedis() { JedisPool pool SaManager.getSaTokenDao().getJedisPool(); log.info(Active: {}, Idle: {}, pool.getNumActive(), pool.getNumIdle()); }权限验证性能// 对于高频接口缓存权限验证结果 SaCheckPermission(user:query) public ListUser queryUsers() { String cacheKey perm_cache_ StpUtil.getLoginId(); return CacheUtils.get(cacheKey, () - { return userService.queryAll(); }); }5.3 扩展开发指南自定义Token生成Component public class MyTokenGenerate implements SaTokenGenerate { Override public String generate() { return MYTOKEN_ IdUtil.fastSimpleUUID(); } }集成JWT# 启用JWT sa-token.token-stylejwt sa-token.jwt-secret-keyyour_secret_key多账号体系// 定义不同账号类型的Stp逻辑 public class StpAdminUtil { private static final String TYPE admin; public static void login(Object id) { SaManager.getStpLogic(TYPE).login(id); } }在实际项目中使用Sa-Token两年多来最大的感受就是开发效率的提升。以前需要半天实现的权限功能现在可能只需要几行代码。特别是在微服务环境下其分布式会话和网关鉴权方案大大简化了系统架构。对于新项目我会毫不犹豫地选择Sa-Token对于老项目只要有机会我也会推动迁移。这不仅是技术栈的更新更是开发理念的升级。

相关新闻

Python基础语法与核心概念全解析

Python基础语法与核心概念全解析

1. Python基础语法全景概览 Python作为一门简洁优雅的编程语言,其基础语法设计处处体现着"大道至简"的哲学思想。我在初学Python时曾被其简洁的语法所震撼——相比其他语言繁杂的类型声明和复杂的结构定义,Python用最直观的方式表达了编程逻辑…

2026/10/14 17:25:20 阅读更多 →
BGA封装标准化:JEDEC规范与高密度电子组装实践

BGA封装标准化:JEDEC规范与高密度电子组装实践

1. BGA封装标准化的核心价值与行业背景在表面贴装技术(SMT)领域,球栅阵列(BGA)封装标准化是确保高密度电子组装可靠性的基石。JEDEC(固态技术协会)制定的标准体系,从根本上解决了不同…

2026/10/14 21:19:00 阅读更多 →
从公网聊天工具迁移到私有化协同平台的实施步骤

从公网聊天工具迁移到私有化协同平台的实施步骤

在技术社区讨论企业协同平台,最好不要只停留在“产品功能有哪些”。真正影响上线质量的,往往是架构边界、权限模型、运维策略和后续扩展方式。先确认问题边界 私有化协同项目最容易被低估的是部署和运维准备。能安装只是第一步,真正决定上线质…

2026/10/14 21:19:14 阅读更多 →

最新新闻

Gitea局域网服务器Local: No connection could be made because the target machine actively refused it.

Gitea局域网服务器Local: No connection could be made because the target machine actively refused it.

现象:使用Gitea 当git仓库管理A电脑当服务器,B电脑推送,有lfs大文件A电脑可以自己推送。B电脑推送失败:提示git.exe push --progress -- "192" master:master warning: use of unencrypted HTTP remote URLs is not re…

2026/10/15 1:59:12 阅读更多 →
细腻手绘齿轮与机械时钟摆动:SVG 矢量齿形数学建模与 CSS 连续啮合旋转

细腻手绘齿轮与机械时钟摆动:SVG 矢量齿形数学建模与 CSS 连续啮合旋转

在复古手账与蒸汽朋克风格的插画设计中,机械齿轮与单摆时钟一直是最具象征意义的美学符号。 发条拧紧时的金属微颤、大小齿轮齿面之间严丝合缝的连续啮合咬合、老式钟摆在两极点之间迟疑而笃定的回摆……这些机械运动所传达的,不仅是纯粹的工科几何理性&…

2026/10/15 1:59:12 阅读更多 →
软考 系统架构设计师历年真题集萃(56)

软考 系统架构设计师历年真题集萃(56)

接前一篇文章:软考 系统架构设计师系列知识点之杂项集萃(55) 第91题 商业智能关注如何从业务数据中提取有用的信息,然后采用这些信息指导企业的业务开展。商业智能系统主要包括数据预处理、建立()、数据分析和数据展现4个主要阶段。其中数据预处理是整合企业原始数据的第…

2026/10/15 1:59:12 阅读更多 →
【Unity开发】StreamingAssets、Assets Bundle、Resources区别对比

【Unity开发】StreamingAssets、Assets Bundle、Resources区别对比

🐾 个人主页 🐾 阿松爱睡觉,横竖醒不来🏅你可以不屠龙,但不能不磨剑🗡文章一、前言二、StreamingAssets文件夹1. 用途2. 访问方式3. 优点4. 缺点三、AssetBundle1. 用途2. 访问方式3. 优点4. 缺点四、Resou…

2026/10/15 1:59:12 阅读更多 →
ECharts地图JS选型与下钻渲染避坑全指南

ECharts地图JS选型与下钻渲染避坑全指南

简介:一套覆盖全国、地市、区县三级地图的js资源共343个文件,专为ECharts使用设计,压缩包约25.79MB。所有文件按“行政区划代码_省份_城市”命名,涵盖黑龙江、吉林、新疆等多省市,便于按区域定位并引入对应地图。开发者…

2026/10/15 1:59:12 阅读更多 →
STM32U5xx HAL 驱动组件解析:在 Arduino_Core_STM32 中的角色、结构与配置

STM32U5xx HAL 驱动组件解析:在 Arduino_Core_STM32 中的角色、结构与配置

嵌入式固件驱动开发硬件开发 【免费下载链接】Arduino_Core_STM32 STM32 core support for Arduino 项目地址: https://gitcode.com/gh_mirrors/ar/Arduino_Core_STM32 点击查看 免费下载 STM32Cube 是意法半导体(STMicroelectronics)面向整…

2026/10/15 1:58:11 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →