ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Mole用户中心实践:从RBAC权限到多仓多组织的统一认证底座

Mole用户中心实践:从RBAC权限到多仓多组织的统一认证底座 Mole这个示例工程说白了就是一套“用户中心”的落地样板间。我在企业里做内部系统时最头疼的就是每个系统都有一套自己的账号体系离职了老系统还在飘着新员工入职要挨个系统开账号光想想就头大。Mole要解决的就是这个通病把用户、权限、认证、审计这些公共能力抽出来做成底座业务系统只专注自己的业务。这篇内容我基于常见企业实践讲讲这个示例工程怎么把底座和业务衔接起来涉及多仓、多组织这类典型场景时又会暴露哪些坑。1. 项目背景与示例工程的整体定位1.1 为什么企业需要一个用户中心底座业务系统多了以后没有统一用户中心的日子你是体会不到的。一个最简单的场景员工入职HR系统开了账号但OA、项目管理系统、BI报表平台、知识库全是独立的账号IT得挨个去创建光第一批账号开通就得折腾大半天。如果中途还要调部门、转岗各系统的权限要手动同步一遍漏掉一个要么是权限收不回来要么是数据看不了都是事。Mole用户中心就是要把账号、组织、角色、权限、单点登录这些“公用模块”从各个业务系统里拆出来沉淀成一个独立底座。业务系统只需要对接用户中心注册应用接入认证剩下的用户管理、权限校验、审计日志统统交给底座处理。示例工程的意义就在于它提供了一个标准的“接法”业务系统该怎么对接、user-center该提供哪些接口、数据模型怎么设计都给你一个可以跑的样板而不是一套PPT。1.2 示例工程与业务系统的边界怎么划对接Mole之前先把边界划清楚这是我在实际项目里强调最多的一件事。用户中心管什么管“你是谁”“你能进哪个系统”“你能干什么”。业务系统管什么管“业务单据怎么流转”“业务规则怎么执行”“业务数据怎么存储”。听起来很清晰但实际做起来很容易越界。拿这个示例工程里的场景来说它模拟的是一个“多组织多仓业务系统”。用户中心负责定义用户、部门、角色的顶层结构业务系统负责仓内的收货、上架、库存调整这些业务动作。但“这个用户能不能操作华东仓的波次计划”这个问题是横跨两边的用户中心管“能进波次计划这个菜单”业务系统管“数据范围限制在华东仓”。所以我在实际落地时通常建议权限模型拆成两块功能权限菜单/按钮归用户中心管数据权限哪几个仓、哪几个组织在业务系统内部用用户中心的组织上下文来过滤。这个切入点不做好后面越做越乱。1.3 示例工程适合谁来看不管你是后端开发、架构师还是企业内部的IT负责人这个示例工程都值得花点时间捋一遍。后端开发可以直接把它当成一个接用户中心的参考实现接口怎么调、鉴权怎么接、用户上下文怎么获取都有现成代码。架构师更需要关注的是它的数据模型设计和API边界划分这决定了未来你企业里几十套系统对接时的复杂度。IT负责人如果不懂技术细节重点看它能解决什么业务痛点、上线时业务系统要改多少地方就行。我见过不少团队自己从零写用户体系结果光权限模型就改了三版越改越复杂最后整个系统都捆绑在登录模块上动弹不得。Mole的价值就是先给你一个经过验证的模型省掉自主探索的时间。2. 用户中心的核心模块拆解与设计逻辑2.1 账号体系主账号与应用账号需要分离真实企业里一个员工会在多个业务系统里有身份而且不同系统里的用户ID可能还不一致。示例工程里把账号体系设计成两个层面主账号全局唯一和应用账号系统内唯一。主账号就是员工在企业内的唯一身份绑定手机号、邮箱、姓名这些基本属性。应用账号则是主账号在每个业务系统里的“分身”。为什么非要拆两层直接用一个用户ID不行吗说个实际的例子你有一个供应商的外部人员他在供应商协同系统里是“供应商操作员”在内部OA里是“外部访客”如果只用一个身份他的权限就得两套合并安全上很难控制。拆开后每个应用账号可以独立分配该应用内的角色主账号统一管控密码策略和启用状态。离职时直接锁主账号所有应用账号同步失效。这个模型看起来简单但能解决非常多真实管理问题。2.2 组织模型部门树、职务、职级不能全揉在一起组织模型是用户中心最容易做烂的地方。很多系统把部门、岗位、职级全塞到一张表里最后维护成本极高。示例工程里把组织维度拆成了三个独立概念部门树解决汇报关系和数据归属比如“供应链中心-华东仓储部”。岗位解决职责分工比如“仓库主管”“上架员”。职级解决职级序列比如P6/M2和技术权限无关。为什么要分开我给你讲个真实踩坑经历。早期我们系统里把“主管”这个岗位做成了角色结果公司组织调整华东仓储部的主管换了人但新主管还没到岗老主管的权限又不能立刻回收业务就出现了真空期。如果把“岗位”和“人”的绑定关系由组织架构模块负责主管离职自动解除绑定再配合备用审批人机制就不会出现这种情况。2.3 RBAC权限模型解决角色爆炸要靠权限点粒度设计用户中心一般都会用RBAC模型也就是用户-角色-权限三层关系。但RBAC不是万能的你授权给“仓库主管”的角色这个角色到底能干什么示例工程里的做法是权限点划分到“按钮级”比如“查看库存”“导出库存报表”“审核盘点差异”。这里有一个非常实用的经验权限点命名一定要用“资源动作”的格式比如inventory:query、inventory:export、inventory:audit。我见过用canEditStock这种动词开头的后期加权限点的时候能把你纠结死。用资源开头后面扩展资源也好排序代码里做权限判断也好写。角色建议分两层通用角色和业务角色。通用角色比如“系统管理员”“审计员”跨系统复用业务角色比如“华东仓收货员”只在特定应用内有效。这两层分开管理员在给业务角色配权限时才不会一不小心把审计权限也授出去。2.4 认证机制与令牌生命周期管理示例工程里的认证推荐走标准OAuth2授权码流程。业务系统引导用户跳转到用户中心登录页登录成功后换token再带着token访问业务系统。JWT用不用我用下来建议是accessToken用JWT、refreshToken用不透明字符串理由下面讲。JWT可以无状态校验业务系统拿到token后本地解析就能拿到用户ID和权限不用每次请求都去用户中心查一次数据库性能好。但JWT有一个致命的缺点没法主动失效。员工离职后他的token只要没过期理论上还能继续访问业务系统。所以refreshToken必须做成有状态、可以吊销的它的有效期更长专门存在用户中心redis里调注销接口时直接删掉。accessToken有效期设短一些比如30分钟就算泄露影响窗口也小。注意这个项目里有个我强烈推荐的细节accessToken里只放用户主账号ID和会话ID不要放一堆权限和用户信息进去。权限是高频变化的你前脚把权限点加到token里后脚管理员改了角色token里的旧权限得等到刷新才更新安全上容易出问题。3. 示例工程的实操搭建与核心实现3.1 目录结构与关键模块初始化拿到示例工程先把目录结构搞清楚。我的建议是从下往上读先看common再看model最后看controller这样思路最顺。工程目录一般长这样mole-sample-system ├── mole-user-center-client // 用户中心SDK客户端 ├── mole-business-module // 业务模块示例业务 │ ├── controller // 接口层 │ ├── service // 业务逻辑层 │ ├── repository // 数据访问层 │ └── model // 实体/DTO/VO ├── mole-common // 公共工具、常量、异常 └── config // 应用配置业务模块和用户中心client分开是防止业务代码直接和用户中心内部接口耦合。业务系统只需要依赖client内部怎么实现不管后续用户中心升级接口业务系统改个client依赖就完事了。3.2 对接用户中心SDK接入与单点登录配置SDK接入一般分两步配置认证参数、接入用户上下文解析。配置参数主要是这些mole: user-center: base-url: http://user-center.mole.internal client-id: mole-sample-system client-secret: xxxxxx redirect-uri: https://sample.mole.com/callback注意几个坑redirect-uri必须和用户中心后台登记的回调地址完全一致多一个斜杠都不行报错的时候第一优先级检查这里。client-secret属于敏感信息不要写进代码仓库建议配置中心管理。用户上下文解析这一步示例工程里一般在拦截器中实现。用户带着token访问业务系统拦截器先解析token拿到用户ID然后再从Redis或本地缓存拿用户的组织、角色、权限明细封装成CurrentUser对象放到ThreadLocal里供后续业务逻辑使用。我在实际项目里会在过滤器里做一次全局的token格式校验如果Authorization头不存在或格式不对直接返回401别等到controller里再判断避免业务代码里到处写校验逻辑。3.3 数据权限落地多仓场景下如何用组织上下文过滤很多示例工程往往只做功能权限但真实业务系统真正难的是数据权限。这个示例工程特别好的点在于它演示了多仓业务场景下如何用用户中心返回的组织上下文做数据隔离。用户的组织上下文从用户中心接口拿到后大概是这样的结构{ userId: u-1024, tenantId: t-81, defaultWarehouse: WH-EAST-01, authorizedWarehouses: [ WH-EAST-01, WH-EAST-02, WH-NORTH-01 ] }业务系统查库存数据时强制拼上仓库过滤条件而不是让前端传仓库参数。ListInventoryBO inventoryList inventoryRepository.selectByWarehouses( currentUser.getAuthorizedWarehouses() );这里有个必须警惕的细节数据权限过滤必须在SQL层做不能只靠前端隐藏。反例是很多系统在前端根据权限隐藏仓库选项然后后端接口只接收仓库ID恶意用户直接构造请求传其他仓库ID数据就泄露出去了。凡是涉及数据权限控制必须“后端强制过滤”前端只是提供更友好的交互。3.4 多租户与多组织的处理示例工程里如果涉及多组织一般会有一个tenant_id字段贯穿所有业务表。实现上可以在MyBatis-Plus的拦截器里自动填充也可以在Mapper层手动拼接条件。但注意多租户不能一刀切做一个全局拦截器就完事。有些表你想让租户间共享比如区域字典表、公共配置表拦截器一律自动拼tenant_id反而麻烦。所以在实现上我一般建一个注解IgnoreTenant标注在白名单表上让拦截器跳过。这个细节虽然不起眼但做过多租户的人都会懂这是个多重要的处理。3.5 示例工程的部署配置与环境要求示例工程要跑起来依赖三样东西MySQL、Redis、用户中心实例。Mysql主要存业务表和用户中心SDK缓存表Redis存会话token和验证码。部署时的配置建议配置项建议值说明spring.redis.timeout3000ms超时设短点宁可让接口报错也不能无限等mole.user-center.token-expire30maccessToken有效期示例工程建议值mole.user-center.refresh-expire7drefreshToken有效期server.servlet.session.timeout30m会话超时兜底配置数据库连接池maximum-pool-size: 20控制连接数防止压垮DB全套工程跑起来的内存占用一般控制在1GB内单体环境完全够用。4. 多仓业务接入用户中心的实战要点4.1 多仓业务的数据模型与仓库业务归属设计多仓业务系统接入用户中心除了标准RBAC权限外仓库本身也有自己的业务归属。仓库在组织架构里挂到哪个部门下、仓库管理员归属哪个岗位、各地区仓的业代归属哪个销售大区这些归属关系我还是建议直接复用用户中心的部门树而不是业务系统单独建一张仓库人员关系表。否则后续“人员异动调仓”时两边数据同步的一致性很难保障。WMS/LMS这类系统现在也讲究多国多仓部署这其实是和用户中心联动性很强的场景。海外仓的库内作业人员、报关人员、第三方仓储人员都需要以外部成员的形式在主账号体系里管理Mole这种主账号与应用账号分离的模型天然适合这种场景。海外仓系统选型时如果底层没有一个稳定的账号权限底座多国合规、人员隔离、操作留痕都会成为很大的隐患。4.2 多组织、多仓下的仓库级权限控制最佳实践仓库级权限控制在WMS系统里是业务刚需。华东仓的仓管员不应该看得到华南仓的库存更不能操作华南仓的波次。这个就是典型的数据权限不是功能权限。实现时我倾向于在业务系统内建一张warehouse_permission表字段包括user_id、warehouse_id、permission_level。这张表的数据不自己维护哪来的从用户中心拿到用户所属组织和岗位后通过匹配岗位对应的仓库归属范围自动算出来。有人可能会问为什么不在用户中心里直接维护因为仓库是业务系统特有的实体用户中心不应该成了一个业务模型的超级大杂烩。用户中心给的是“组织关系”业务系统翻译成“仓库权限范围”职责清晰谁也替代不了谁。4.3 用户中心与业务系统协作的接口设计模式两者的接口协作模式我总结为三类认证类登录、注销、校验token、刷新token。这类接口频率高要求低延迟一般走网关加上限流。查询类获取用户信息、获取组织架构、获取角色权限。这类接口适合加缓存比如本地缓存5分钟或Redis缓存15分钟。组织架构变了最多延迟15分钟生效业务可以接受。事件类用户离职、角色变更、组织调整用户中心通过消息队列发事件业务系统监听后更新自己的本地读模型。实战经验不要频繁调用用户中心的用户详情接口。业务系统在本地务数据库里冗余一份sys_user_snapshot表用户在用户中心变了通过事件更新快照。查询性能提升一个数量级还避免了对用户中心接口的冲击。4.4 WMS多仓系统选型与账号体系的联动回到前面提的多国多仓海外仓WMS选型有些团队把账号权限放在最后考虑觉得“后面接一个统一登录就行了”这是最容易翻车的地方。我测评4款主流WMS系统时专门做了一个对比表格判断维度里数据权限和组织模型这两项权重非常高系统组织模型数据权限控制粒度账号体系对接能力系统A单层公司仓库仅仓库级支持标准OAuth2需二次开发系统B多级组织树仓库库区货主预置接口支持自定义映射系统C公司部门仓库仓库级仅支持账号密码同步系统D集团公司仓库自定义数据权限脚本对接灵活但门槛高结论是选WMS不要光看功能列表要看它底层账号体系是否能支撑组织合并、人员调岗、外部供应商/承运商账号管理。否则业务发展到多国多仓时WMS会让你卡在人员权限管理上寸步难行。5. 模拟业务从登录到业务单据入库的完整链路5.1 登录与令牌获取流程我用示例工程模拟一个“华东仓收货员小张”从登录到做收货业务的完整流程。小张在浏览器打开业务系统未登录跳转到用户中心登录页。输入账号密码用户中心校验通过后生成accessToken和refreshTokenaccessToken 30分钟有效refreshToken保存到Redis7天有效。用户中心带着accessToken回调到业务系统的/callback接口业务系统用授权码换token然后获取用户信息。业务系统拿到用户信息后不再走用户中心数据库而是落地到本地快照表并初始化一个CurrentUser上下文。5.2 业务请求的权限链路处理小张点开“收货单创建”页面前端请求POST /api/receipts。请求拦截器解析accessToken取出用户ID从本地缓存加载用户角色和权限。权限判断发现小张拥有receipt:create权限点放行。Controller层拿到当前用户上下文中的归属仓库列表校验请求体中的warehouseId是否在授权范围内不是则返回403。通过后创建收货单SQL写入时强制带上tenantId和warehouseId。5.3 前端按钮级别控制与后端数据过滤小张能看到哪些页面、能点哪些按钮依赖用户中心返回的权限列表前端根据权限进行菜单渲染。但是注意前端隐藏只是用户体验优化后端的权限判断和仓库过滤才是安全的关键。完整流程里还要注意一个点小张浏览器里的登录态过期了怎么办前端发现访问接口返回401需要拿着refreshToken去用户中心换新的accessToken这个过程对用户无感知。如果refreshToken也过期了则跳到登录页。这个token刷新逻辑建议做成前端全局的不要在每个接口里单独写。5.4 审计日志与用户行为记录审计日志是示例工程里容易被新手忽略的模块。合规要求越来越严格的今天谁在什么时间对什么数据做了什么操作一定要能追溯。用户中心提供的审计能力包括登录日志、权限变更日志、异常操作日志。业务系统也要记录业务操作日志比如收货单创建、状态流转、审核操作。日志字段至少包含用户主账号ID、应用账号、IP、操作时间、操作类型、操作对象、操作前后数据快照可选。这些数据我建议异步写入不要阻塞主业务流程。有次我们一个客户要求审计日志必须落库覆盖关键业务动作当时异步消息队列扛不住高峰流量后来把日志批量写入改成Redis队列缓冲加批量消费才把性能问题解决。注意业务操作日志不建议用Log4j直接打印到应用日志文件里查询审计记录时没法查。要独立落在审计表或独立的日志存储中。6. 常见问题排查与踩坑心得6.1 Token过期导致调用用户中心接口401最常见的问题业务系统拿着accessToken调用户中心接口用户中心返回401。排查思路先确认accessToken是否在有效期再看业务系统的时钟和用户中心时钟是否NTP同步。JWT校验如果依赖系统当前时间和签发时间时钟漂移会导致大量验证失败我当时遇到过一次排查了老半天最后发现是运维没有配置时钟同步。6.2 回调地址不一致导致登录失败业务系统把redirect-uri配置成https://sample.mole.com/callback但用户中心后台登记的是https://sample.mole.com:8443/callback端口不一样也会报错。这种问题日志里报的还是通用OAuth错误非常容易让人绕弯。直接对比两边的回调地址一个字符都不能差。6.3 权限变更后前端不生效管理员在用户中心修改了小张的角色把小张的“收货单删除”权限去掉了但小张刷新页面还是能看得见删除按钮。原因大概率是前端的权限列表缓存在本地没有实时刷新。解决方法是登录时拉取一次前端会话启动时再校验一次或者用户中心在权限变更事件里发一个消息前端收到后主动刷新。6.4 用户离职后仍能访问业务系统这个场景最可怕。系统A的token有效期设置为7天小张离职了管理员把主账号停了但小张的token还没到期他依然能访问业务系统。用户中心能控制主账号登录但已经签发的JWT很难主动作废。所以一定要配置短时效的accessToken加可吊销的refreshToken并确保业务系统每次收到请求都去校验用户主账号的启用状态至少用Redis缓存用户状态并设置很短的时间比如1分钟。有个更稳妥的做法用户中心在“用户禁用事件”中发消息业务系统消费事件后把该用户的本地session全部强制失效。这样哪怕token还有效业务系统在拦截器阶段发现本地session不存在直接拒绝。6.5 组织调整导致数据权限变化延迟小张从华东仓调到华南仓用户中心里组织关系变了但业务系统的CurrentUser还缓存了旧的仓库授权范围。结果他去操作华南仓的单据说没有权限操作华东仓的单据又还能操作。解决方案组织变更时用户中心发一个org-change事件业务系统监听后立刻刷新用户上下文并同步更新数据权限缓存。6.6 性能优化与缓存策略用户中心相关查询如果每次都打DB系统压力一定扛不住。示例工程的优化方向用户基础信息缓存30分钟角色权限缓存10分钟组织树缓存1小时并加版本号。同时用本地Caffeine Redis二级缓存先查本地本地没有再去RedisRedis没有再去DB。缓存更新采用主动失效加定时兜底刷新。7. 扩展建议把示例工程改造成产品级底座7.1 从示例工程到生产环境需要补强的点示例工程毕竟是示例直接上生产的话有几个点一定要补。第一个是网关层统一认证与鉴权业务系统的所有请求入口建议经过API网关网关做token校验和IP白名单业务系统内再配合本地拦截器做权限细节校验。第二个是操作审计字段要全出现合规纠纷时数据不全是最大的坑。第三个是密钥管理的规范化client-secret要放在配置中心或密钥管理服务中并定期轮换。7.2 多租户与级联组织扩展示例工程如果只做了单租户那扩展成SaaS底座时需要考虑级联组织。一个集团下有多个公司每个公司下有多个仓库租户下挂着完整组织树。用户中心最好能支持租户维度配置“数据级联可见”比如集团管理员看全集团数据公司管理员看本公司数据仓库管理员看本仓数据。这个规则不用写死在代码里可以做成数据字典。7.3 向业务系统输出组织架构同步能力产品级的用户中心除了账号权限还应该提供组织架构的“事件流”输出能力比如企业微信、钉钉、飞书的组织架构实时同步。这块的异步消费和幂等处理比较关键重复同步消息不能造成脏数据。8. 写在最后的实操体会Mole这个示例工程我认为最大的价值不在于代码本身有多惊艳而在于它对用户中心和业务系统的边界拿捏得很清楚。它告诉后来者什么东西该放底座什么东西该放业务侧一条一条理得清清爽爽。我在实际项目中踩过最多的坑恰恰就是边界不清晰用户中心功能越做越宽最后变成一个啥都管却啥都管不好的巨型系统。建议所有准备对接近似底座的同学拿到示例工程后不要急着敲代码先花半天时间把它的数据模型梳理一遍再对照自己业务系统的组织模型想想哪些要改、哪些直接复用。磨刀不误砍柴工这一步想透了后面的对接就是填表和调接口的事。另外我分享一个小技巧排查权限问题时不要凭肉眼去核对角色和权限点做一个SQL直接把用户、角色、权限点、资源范围四张表join出来一眼看全链路。别看这个办法土真能帮你省下大量排查时间。
返回列表