首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >村务后台账号角色七八种,登录态和权限边缘情况该怎么处理?

村务后台账号角色七八种,登录态和权限边缘情况该怎么处理?

原创
作者头像
小小码农爱奋斗
发布于 2026-10-23 10:56:28
发布于 2026-10-23 10:56:28
300
举报

一、一个镇十七个村,账号角色比想象中多

数字乡村项目做到第二个镇的时候,后台账号体系已经不再是「一个管理员加一批普通用户」那么简单。镇一级有管理账号,村一级有村务账号,下面还挂着网格员、党员、村民代表、普通村民,加上一个只看数据的观摩账号,角色加起来七八种。同一个村民可能在两个村都有身份,同一个网格员管着两百多户,镇管理员要能临时接管某个村的账号做排查。这套账号跑在 SpringBoot 2.5 的单体后端上,前端是 Vue3.2 的管理台和小程序两端,登录态的维护一开始没当回事,直到三月份镇里做了一次账号大调整,问题集中爆了出来。

那天下午运维收到四条反馈。第一条,某个村的村务账号被停用之后,他手机上还在正常刷数据,停了像没停一样;第二条,给网格员新加了两个菜单权限,他重新登录了三遍还是看不到;第三条更尴尬,镇管理员测试的时候把自己在另一个浏览器的会话顶掉了,还以为是系统出了毛病。四条反馈指向的是同一层的东西,登录态和权限的判定逻辑,当时这块代码散在三个拦截器里,谁也说不清完整链路。

我们先把现象抄在白板上排了一遍,发现这四条其实分两类。前两条是权限的生效时机问题,系统里权限数据已经改了,但用户那边拿到的还是旧结论。后两条是会话的管理问题,多端登录、顶号、账号停用这些状态变化没有及时传导到已发的凭据上。分开看都是小问题,合在一起说明这一层缺一个统一的判定入口。

二、问题的本质是权限结论被缓存了两份

把代码翻出来看,权限结论在两个地方被各自缓存了一份。第一份在网关层的拦截器里,用户登录时把他拥有的菜单和按钮权限算出来,塞进会话的属性里,后续每个请求都直接读这份快照。第二份在前端,路由表按权限生成之后存在本地,菜单渲染全靠它。这两份缓存都没有和权限表的变更建立任何联系,改了库里的数据,两份快照纹丝不动,用户自然看不到变化。

直觉做法是在权限变更的时候写一段代码去清缓存,改账号的清一次,改角色的清一次,改角色和账号的关联关系再清一次。这条路能跑通,但变更入口有五六个,漏掉一个就是又一条工单,而且清缓存和改数据不在一个事务里,中间的窗口期照样会出脏读。我们想要的是把「权限结论什么时候失效」这个问题从各个业务代码里拿走,交给一个统一的机制去回答。

会话那半边的问题类似。当时每个接口自己判断登录状态,判断方式是看请求头里有没有令牌,有就放行。账号停用只是改了个状态字段,已发出去的令牌依然有效。多端登录也没有约定,同一账号在电脑和小程序各拿一个令牌,谁也不知道对方的存在,顶号行为完全随机。

三、两条路的代价都摆过

第一条路是上 Spring Security,全家桶式的方案,过滤器链、认证、授权、记住我全都内置。代价是这套东西的接入面太大,我们要按它的模型改造整个请求链路,光是把现有的几个拦截器迁移进过滤器链就估了一周,后面每次加新端都要重新理解一遍它的配置体系。对一个五六个人维护的项目来说,这份复杂度换来的能力我们用不上几成。

第二条路是换 SaToken,它把会话管理和权限判定做成了轻量的组件,权限数据怎么算完全由业务侧提供,它只负责存、查、踢、续这些通用动作。代价是权限计算要自己写,角色和权限的查询逻辑得在业务层实现一遍,另外它的文档偏使用向,一些边界行为要自己读源码确认。两条路摆在一起,我们选了第二条,理由很简单,我们要补的是会话管理这一块短板,不是要重买一整套安全框架。

选型的时候还考虑过第三种,自己写令牌加 Redis 的裸方案。这条路自由度大,但顶号、续期、临时令牌这些细节全要手工实现,每一个都是别人踩过的坑,收益却和用现成组件差不多,讨论了半小时就放弃了。

四、登录态收进一个统一的判定入口

改造的第一步是把散在三个拦截器里的判断收拢。SaToken 的路由拦截器挂到最外层,先做登录校验,再按路由前缀做角色校验,业务方法内部的细粒度判断交给注解。权限数据由业务侧实现 StpInterface 提供,查询逻辑就是三张表的联查,账号、角色、权限项,结果按账号维度缓存十分钟。这套认证结构后来收进了万村乐数字乡村的统一鉴权模块,镇村两级后台共用同一个入口,不再各算各的。

多端登录的约定是当时讨论得最多的一处。最终定的规则分三档,村民账号允许电脑加手机同时在线,村务账号同一时间只保留一个端,镇管理账号在设备变更时必须重新验证。SaToken 的并发登录配置按账号类型区分,这段逻辑写在登录入口里,一个账号类型一个配置项。账号停用的传导也顺了,状态字段变更时直接踢掉该账号的全部会话,被停用的账号下一次请求就会拿到明确的未登录错误,而不是继续读到旧数据。

权限缓存的有效期最后定为五分钟,比最初设计的十分钟短。这是按工单的容忍度倒推的,管理员给网格员加了菜单,五分钟内生效可以被接受,超过十分钟就会有人反复刷新怀疑没保存上。变更入口那里还是补了一个主动失效的钩子,改角色的时候顺手把关联账号的缓存清掉,主动失效兜大头,五分钟有效期兜漏网的。

五、配置项和几段关键约定

会话相关的配置最后收敛成一张表,一共八个键值,每一项都有明确的业务理由,不是抄默认值抄来的。这张表贴在团队的文档首页,改任何一项都要在旁边补一句为什么改,半年下来这张表成了排查登录问题的一手依据。

active 这一项是后面才补的,含义是「连续无操作多久之后要求重新登录」。村民账号挂七天没动就下线,村务账号两个小时没操作就要重新登。上线第一个月就有村务账号反映中午吃完饭回来要重新登录,我们把 active 从一小时调到了两小时,这个值没有标准答案,只能在安全和抱怨之间找平衡。

令牌在两端传递方式也不一样。电脑端走请求头,小程序端因为框架对请求头的支持不一致,统一改成了请求头加参数双通道,服务端按固定顺序读取。这个约定写在接口文档的公共部分,新端接入时照抄即可。

六、三个坑,每个都付过学费

第一个坑是权限缓存忘了算角色关联。权限查询的三张表联查没问题,但缓存键只用了账号标识,角色和权限项的变更落在另外两张表上,主动失效的钩子第一次只挂在账号编辑上。结果就是改角色权限不生效的工单又来了两单。根因是失效钩子的粒度和数据变更的粒度没对齐,改法是把钩子下沉到「角色改权限」「账号改角色」两个变更点,各自清掉受影响的账号集合。

第二个坑是顶号策略误伤了定时任务。村务账号只允许一端在线,有个村用这个账号跑数据导出的脚本,白天有人登录后台,晚上的脚本就被顶下线了。现象是脚本连续三天没产出数据,查日志才发现登录接口返回了被顶下线的错误。根因是并发登录控制只按账号算,没区分人和程序。改法是给这类账号单独开了一个「接口账号」类型,不受单端限制,但权限只给到导出这一个菜单。

第三个坑是临时令牌的续期。观摩账号用临时令牌发放,设计上是七十二小时有效,上线后有观摩人员反馈第三天下午页面打不开了。查下来是临时令牌的有效期从发放时刻起算,中间没有任何请求去续期,而我们默认它和普通令牌一样有活跃续期。根因是想当然地沿用了普通会话的心智模型,改法是在临时令牌的每次使用处显式续期,并把剩余时间回传给前端做倒计时提示。

七、这层解决不了的事

登录态和权限判定收拢之后,前面那四条工单再没复发过,但有两类问题明确不在这层的射程里。一类是数据级的行权限,某个网格员只能看他管的两百户数据,这个判定发生在每一条查询的拼装里,和登录态无关,需要单独的一层过滤规则去处理。另一类是敏感操作的事前审批,镇管理员要临时接管村账号这类动作,属于业务流程,不是鉴权组件该管的,我们后来用操作留痕加二次确认把它挡在了流程层。

还有一句话要提前说清楚,换任何一个鉴权组件都解决不了「权限设计本身不合理」的问题。我们见过把三十个权限项全塞给一个角色的做法,那不是缓存和会话的问题,是角色划分要重新梳理,这层代码再干净也帮不上忙。

八、小结

回头看这次改造,真正值钱的不是换了哪个组件,而是把「权限结论何时失效」「会话何时作废」这两个问题从各处业务代码里拿走,交给两个明确的机制回答,业务代码从此只声明自己需要什么权限,不再操心它从哪来、什么时候会变。配置表里每一项都对应一条真实的工单,这个对应关系比任何文档都有说服力。

这些做法后来都进了万村乐数字乡村,成了装完就能用的默认配置,十七个村的账号再出现生效时机的反馈,我们先查变更点,再查配置,两个小时之内基本都能定位。权限这层没有一劳永逸的设计,只有把每个不确定的点变成一条写死的约定,账号体系才敢往多角色多端的方向继续长。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档