Sa-Token 实践:将权限数据放入缓存,为 StpInterface 减负

发布时间:2026/9/13 9:07:29

Sa-Token 实践:将权限数据放入缓存,为 StpInterface 减负 Sa-Token 实践将权限数据放入缓存为 StpInterface 减负【免费下载链接】Sa-Token✨ 开源、免费、一站式 Java 权限认证框架让鉴权变得简单、优雅—— 登录认证、权限认证、分布式 Session 会话、微服务网关鉴权、SSO 单点登录、OAuth2.0 统一认证、jwt 集成、API Key 秘钥授权、API 参数签名项目地址: https://gitcode.com/GitHub_Trending/sa/Sa-Token在 Sa-Token 中所有权限/角色校验最终都会回调你实现的StpInterface接口框架默认不提供权限数据缓存如果每次都查库高频鉴权接口下数据库压力会迅速放大。本文讲解如何用SaManager.getSaTokenDao()的getObject/setObjectAPI 把角色与权限数据按[账号id - 角色id - 权限列表]两级结构放入缓存并结合源码说明该缓存模型为何优于直接缓存账号权限全集的粗暴做法。读完你可以完整落地权限缓存版StpInterface实现理解其底层组件调用链并在权限变更时正确失效缓存。一、为什么框架默认不缓存权限数据先明确 Sa-Token 的鉴权数据流。权限校验 APIcheckPermission、hasPermission等并不直接查库而是委托给权限数据源加载接口StpInterface。该接口的 Javadoc 明确写明了这一设计边界见 StpInterface.java在使用权限校验 API 之前你必须实现此接口告诉框架哪些用户拥有哪些权限。框架默认不对数据进行缓存如果你的数据是从数据库中读取的一般情况下你需要手动实现数据的缓存读写。StpInterface只有两个核心方法外加一个可选的封禁判断isDisabled默认方法public interface StpInterface { /** 返回指定账号id所拥有的权限码集合 */ ListString getPermissionList(Object loginId, String loginType); /** 返回指定账号id所拥有的角色标识集合 */ ListString getRoleList(Object loginId, String loginType); /** 返回指定账号 id 是否被封禁default 方法默认不封禁 */ default SaDisableWrapperInfo isDisabled(Object loginId, String service, String loginType) { return SaDisableWrapperInfo.createNotDisabled(); } }再看调用链以 StpLogic.java 为例每次校验都会穿透到数据源// 获取指定账号的权限码集合 —— 每次调用都会进入 StpInterface public ListString getPermissionList(Object loginId) { return SaManager.getStpInterface().getPermissionList(loginId, loginType); } // 判断指定账号是否含有指定权限 —— 依赖上面的 getPermissionList public boolean hasPermission(Object loginId, String permission) { return hasElement(getPermissionList(loginId), permission); }也就是说每一个hasPermission/checkPermission/checkRole背后都是一次StpInterface回调。如果你的实现里每次都查数据库那么鉴权 QPS 就等于权限查询 QPS。另外要注意两点默认行为未实现StpInterface时所有权限校验都判负。组件获取逻辑见 SaManager.javagetStpInterface()在容器中找不到实现 Bean 时会兜底创建StpInterfaceDefaultImpl而 StpInterfaceDefaultImpl.java 的两个方法都返回空集合即用户不具有任何权限和角色。权限数据源与持久化存储解耦。Sa-Token 用SaTokenDao作为统一的读写抽象键值 对象两级 API你的缓存实现可以直接复用它而不必自己维护一套 Redis 客户端。官方 demo 中有一个不带缓存的最简实现可供对照例如 sa-token-demo-springboot 的 StpInterfaceImpl直接硬编码返回权限码与角色列表仅用于演示。生产环境下正是需要把它改造为先查缓存、未命中再查库的版本——这正是本文的主题。二、完整实现带缓存的 StpInterface下面是参考实现的核心思路与完整代码在getRoleList与getPermissionList中都先按约定 key 从SaTokenDao读缓存未命中时查库并通过setObject回填缓存有效期设为 30 天60 * 60 * 24 * 30秒。/** * 自定义权限验证接口扩展 */ Component public class StpInterfaceImpl implements StpInterface { // 返回一个账号所拥有的权限码集合 Override SuppressWarnings(unchecked) public ListString getPermissionList(Object loginId, String loginType) { // 1. 声明权限码集合 ListString list new ArrayList(); // 2. 遍历角色列表查询拥有的权限码 for (String roleId : getRoleList(loginId, loginType)) { ListString permissionList (ListString) SaManager.getSaTokenDao().getObject(satoken:role-find-permission: roleId); if (permissionList null) { // 从数据库查询这个角色 id 所拥有的权限列表 permissionList ...; // 你的数据库查询逻辑 // 查好后set 到缓存中 SaManager.getSaTokenDao().setObject(satoken:role-find-permission: roleId, permissionList, 60 * 60 * 24 * 30); } list.addAll(permissionList); } // 3. 返回权限码集合 return list; } // 返回一个账号所拥有的角色标识集合 Override SuppressWarnings(unchecked) public ListString getRoleList(Object loginId, String loginType) { ListString roleList (ListString) SaManager.getSaTokenDao().getObject(satoken:loginId-find-role: loginId); if (roleList null) { // 从数据库查询这个账号id拥有的角色列表 roleList ...; // 你的数据库查询逻辑 // 查好后set 到缓存中 SaManager.getSaTokenDao().setObject(satoken:loginId-find-role: loginId, roleList, 60 * 60 * 24 * 30); } return roleList; } }几个关键细节缓存 key 的规划satoken:loginId-find-role:{loginId}存放账号 → 角色列表satoken:role-find-permission:{roleId}存放角色 → 权限列表。前缀带业务语义便于在 Redis 中排查与批量清理。两级回源getPermissionList先取角色列表可能回源一次再逐个角色取权限列表每个角色最多回源一次。账号的权限全集是各角色权限的并集。超时时间setObject的第三个参数单位是秒示例取 30 天。SaTokenDao对 timeout 的语义在 SaTokenDao.java 中有明确定义/** * 写入 Object并设定存活时间 单位: 秒 * param timeout 存活时间值大于0时限时存储值-1时永久存储值0或小于等于-2时不存储 */ void setObject(String key, Object object, long timeout);除大于 0 的限时存储外还有-1永久存储、0或 -2不存储两种特殊值getObject未命中时返回nullSaTokenDao.java示例代码正是以 null作为回源判断依据。缓存介质由 SaTokenDao 决定。SaManager.getSaTokenDao()的兜底逻辑见 SaManager.java未注册SaTokenDao实现时会使用默认的内存实现SaTokenDaoDefaultImpl单机、随进程生命周期。在多节点部署下建议接入 Sa-Token 官方的 Redis 插件如 sa-token-plugin/sa-token-alone-redis、sa-token-plugin/sa-token-redisson此时权限缓存数据与其他 Sa-Token 会话数据共用同一 Redis无需额外引入存储。三、缓存模型设计为什么是两级而不是账号直接映射权限全集这是本文最值得借鉴的设计判断。一个直觉做法是登录时直接写入账号id - 权限列表一条缓存StpInterface中一次读取搞定// 直觉做法不推荐 // 登录时RedisUtil.setValue(账号id, 权限列表); // 校验时ListString list RedisUtil.getValue(账号id);直接粗暴却有一个严重问题文档给出的论证是系统权限架构通常是 RBAC 模型权限与用户没有直接关系而是用户拥有指定角色角色再拥有指定权限这种拥有关系是动态的随时可修改一旦修改对应关系就需要同步修改或清除缓存。假设系统中有十万个账号属于同一个角色当你调整这个角色的权限时若采用账号id - 权限列表的扁平缓存就要同时清除十万条账号级缓存——同一时间批量清除大量缓存极易引起 Redis 的缓存雪崩。而采用[账号id - 角色id - 权限列表]两级模型时只需要清除或更新角色id - 权限列表这一条缓存变更成本从 O(账号数) 降为 O(1)。一言以蔽之权限的缓存模型需要跟着权限模型走角色缓存亦然。缓存的组织结构应该镜像业务模型的共享点——角色正是权限的共享单元把它作为缓存粒度权限变更天然收敛到一个 key 上。四、上线检查清单结合本文实现与源码行为落地时建议核对检查项说明缓存失效策略修改角色-权限关系时删除satoken:role-find-permission:{roleId}修改账号-角色关系时删除satoken:loginId-find-role:{loginId}SaTokenDao 介质多节点部署时必须接入 Redis 类插件默认内存实现只在单机场景有效超时设置示例为 30 天限时存储若选择-1永久存储则完全依赖主动失效需保证失效逻辑无遗漏未命中回源getObject返回null才回源查库注意与查到空列表区分空列表也会被缓存住兜底行为确认若忘了注册StpInterfaceBean框架会回退到空集合默认实现表现为所有人无权限排查时可先看 StpInterfaceDefaultImpl.java按此结构落地后高频鉴权路径StpLogic.hasPermission→SaManager.getStpInterface()→ 你的实现绝大部分请求都只触碰缓存数据库仅在缓存过期或权限变更后被动回源从而显著降低权限校验对数据库的压力。【免费下载链接】Sa-Token✨ 开源、免费、一站式 Java 权限认证框架让鉴权变得简单、优雅—— 登录认证、权限认证、分布式 Session 会话、微服务网关鉴权、SSO 单点登录、OAuth2.0 统一认证、jwt 集成、API Key 秘钥授权、API 参数签名项目地址: https://gitcode.com/GitHub_Trending/sa/Sa-Token创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/9/13 9:07:29

ntfy 如何用 CLI 订阅主题并为每条消息执行自定义命令

ntfy 如何用 CLI 订阅主题并为每条消息执行自定义命令 【免费下载链接】ntfy Send push notifications to your phone or desktop using PUT/POST 项目地址: https://gitcode.com/GitHub_Trending/nt/ntfy 当你希望某条 ntfy 消息到达时,机器上能自动跑一段处…

2026/9/13 9:07:29

用Romm CHD压缩游戏镜像,省60%空间

用Romm CHD压缩游戏镜像,省60%空间 【免费下载链接】romm A beautiful, powerful, self-hosted ROM manager and player. 项目地址: https://gitcode.com/GitHub_Trending/rom/romm 昨晚朋友在群里问我:“那个 4.7G 的 PS2 镜像,能不能…

2026/9/13 9:57:31

MS-DACAN:面向工况漂移的轴承故障演化诊断模型

1. 这不是又一个“加了注意力机制”的诊断模型——MS‑DACAN 解决的是产线换型时的诊断失灵问题 我第一次在某风电整机厂看到这个场景:同一型号的变桨轴承,在A产线用X品牌传感器采集的数据上训练好的故障诊断模型,部署到B产线后准确率从98.2%…

2026/9/13 9:57:31

SpringBoot二手书交易系统架构设计与实践

1. 项目背景与核心价值二手书交易平台在高校和社区中一直存在旺盛需求。传统线下交易模式存在信息不对称、交易效率低、价格不透明等问题。基于SpringBoot的二手书交易系统95q22正是为解决这些痛点而生。这个项目最核心的价值在于:为买卖双方提供标准化交易流程通过…

2026/9/13 9:57:31

Python回溯算法实战:从原理到LeetCode解题技巧

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

2026/9/13 9:52:31

无人机吊舱单目相机目标定位算法:坐标变换与测距的C++工程实践

简介:面向无人机视觉开发者、吊舱算法工程师及目标定位方向学习者,这份压缩包围绕“无人机吊舱单目相机目标定位”提供一套可运行、易扩展的C工程实现。工程采用模块化结构,含src、include、demo及CMakeLists构建配置,并附带使用说…

2026/9/13 0:01:16

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/13 0:01:16

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/12 6:29:36

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/12 14:32:17

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/12 6:37:43

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码