LoginUser 就放在 Redis(或者 Sa‑Token 的 session)里,每次接口拿到 Token 都会把它拉出来。项目里做权限判断、渲染菜单、记录谁做了什么,几乎都从这个对象开始。
当你请求后端接口,校验流程大致是:网关/过滤器读 Header 的 Token,去 Redis 找对应的 LoginUser,反序列化后把用户信息灌入当前请求的上下文。之后任何地方只要调用
SecurityUtils.getLoginUser(),都会拿到这个对象。前端 Vue 里根据 loginUser.permissions 决定哪些菜单显示、哪些按钮可点,后端又根据同样的数据决定能不能执行某个操作。看着顺手,但细节许多,出错也容易。
LoginUser 里装的东西不少:用户基本信息(id、用户名、昵称)、所属部门、角色列表、菜单标识、按钮权限、Token 字符串、登录时间和过期时间、浏览器 UA、IP、登录地点等扩展字段。把这些都存在一起,接口层、审计系统、风控规则引擎都能直接用。举例:风控要做异地登录、可疑设备拦截,直接用 LoginUser 的 ipaddr、browser、loginLocation 就能下规则;审计要写“谁在什么时候改了哪条数据”,用 LoginUser 的 id、用户名就够了。
再往前看登录流程:前端把用户名密码发到后端,LoginService 验证通过后,会组装一个 LoginUser 对象;然后 TokenService 基于这个对象生成 Token,把 LoginUser 和 Token 一起存到 Redis;返回给前端的是 Token。后续每次请求都把这个 Token 放到 Header,后台就能根据 Token 把 LoginUser 还原回来。要注意的是,LoginUser 必须可序列化,存到 Redis 需要经过序列化和反序列化步骤,版本变化或字段变动容易出问题。
权限变化时要处理好缓存失效。角色或菜单调整后,如果不清理缓存,用户拿到的还是旧权限,前端菜单与后台权限可能不同步。常见做法是:角色变更触发清缓存、或者按角色维度缓存、或者把权限拆成更细的粒度存储。大规模系统还会把权限按系统拆分、或把权限树扁平化,减少单条 LoginUser 的体积。别把用户详情这种超大字段塞进去,Redis 内存会被撑爆。
在微服务场景下,LoginUser 常被转成 JSON 在服务间传递,或者在网关层完成认证后只传一个最精简的用户识别信息。这样一来,服务间的审计和数据权限检查都还能用同一套标识。多租户场景下,LoginUser 也会带上租户 id,用于实现数据隔离和租户级权限。
Token 的管理也有讲究。通过 JWT 做无状态校验方便,但要支持动态续期和强制下线,则需要把 Token 和 LoginUser 存 Redis。若依里有定时任务来做续期策略,移动端常用较长的登录时长,管理后台则短些。要防止长期占用登录状态导致资源浪费,或者出现无法踢出用户的情况,设计续期和失效逻辑时要把这两种场景区分开来。
权限判断的实现层面,除了 Super Admin 的特殊处理,常见是基于角色走菜单和按钮权限校验。前端用 permissions 控制渲染,后端在方法调用处再做一次权限校验,两边都依赖同一套标识体系。把业务侧的用户信息也放进 LoginUser,能让各中间层方便地取用:数据库层拼 SQL 做数据权限过滤、审计层拼日志写“谁、什么时候、做了什么”,风控层把用户的 UA、IP 输入规则引擎评估风险。
有些细节容易被忽视:LoginUser 的大小会影响序列化性能和 Redis 内存;版本兼容要思考字段增减带来的反序列化异常;权限变动后需要触发缓存刷新;Token 未失效也会导致权限延迟生效。这些问题都有对应的解决思路,列如按角色缓存、拆分权限模块、定时任务触发续期、变更时强制清理相关用户的缓存等等。
在若依项目中,如果你在任意接口里看到
SecurityUtils.getLoginUser(),那就别想绕开这个对象。把 LoginUser 用好,许多权限相关的功能能一次性搞定。不过别贪心把所有业务数据都往里塞,设计时要平衡完整性和性能,否则到头来麻烦多。





