B2B2C 商城里,平台要同时伺候好三类人:商家、买家和平台管理员。这三类人的权限边界得划清楚 —— 商家不能看别家的数据,管理员得能统筹全局,买家只能管自己的订单。ZKmall 开源商城用 Shiro 安全框架搭了套多商户权限体系,既能让商家自己管理员工权限,又能保证平台对数据的绝对掌控,还能灵活调整权限,给复杂的多商户场景撑起了技术骨架。

多商户权限模型:三层权限 + 角色体系
多商户的权限管理可不是简单给个 "能看" 或 "不能看",得像切蛋糕一样,把权限分得明明白白。ZKmall 的权限模型分了好几层,每层各管一摊事。
权限分三个维度,缺了哪个都不行。功能权限管 "能做什么",比如商家能不能上架商品、改价格;数据权限管 "能看什么数据",比如商家 A 只能看自己的订单,看不到商家 B 的;菜单权限管 "能进哪个页面",比如普通商家看不到平台的商户管理后台。这三个维度互相配合,比如一个商家有 "改商品" 的功能权限,但数据权限只让他改自己的商品,菜单权限只让他看到自己的商品管理页,三者一起发力,才叫安全。
角色体系让权限分配不用一个个来。ZKmall 把角色分成三类:平台角色(像超级管理员、运营)、商家公共角色(商家管理员、客服)、商家自定义角色(比如有的商家搞个 "秒杀专员")。平台角色权力最大,能看所有商家的数据;公共角色是平台预设的,给商家一些基础权限,比如管商品、看订单;自定义角色是商家自己建的,只能在平台给的权限范围内折腾,比如平台允许改价格,商家才能建个 "价格调整员" 角色。角色和权限的关系存在中间表里,改权限时改角色就行,不用挨个改用户,方便多了。
商户数据隔离是底线,绝对不能串。数据库里所有跟商家有关的表 —— 商品、订单、用户 —— 都加了个 merchant_id 字段,一看就知道是谁的数据。应用层面,Shiro 的过滤器会自动在查询时加上 merchant_id 条件,商家查订单时,SQL 里自动带上 "where merchant_id = 自己的 ID",想查别人的都查不了。缓存的时候也机灵,Redis 的键前面都加上商户 ID,比如 "product:1001:123",1001 是商户 ID,123 是商品 ID,这样不同商家的缓存就不会混在一起。从数据库到缓存,全链路隔离,想越权都难。
权限继承有规矩,不能想干嘛就干嘛。平台给商家一个基础权限包,商家可以在里面拆拆合合,建子角色,但绝对不能出圈。比如平台给了 "改商品信息" 的权限,商家可以建个角色只给 "看商品" 的权限,或者建个角色能 "改商品所有信息",但绝对建不出能 "看平台结算数据" 的角色 —— 那是平台的自留地。这样既给了商家自主权,又守住了平台的底线。

用 Shiro 实现动态权限:改权限不用重启系统
Shiro 是个成熟的安全框架,但默认的权限管理有点死板,改个权限得重启系统。ZKmall 给它动了些手脚,让权限能实时变,还能管得特别细。
动态加载权限,改完立马生效。以前 Shiro 的权限一般是启动时从数据库或配置文件读进来,改了权限得重启才能生效,商家哪等得起。ZKmall 自己写了个 DynamicPermissionResolver,权限一改,Redis 里的缓存就跟着更,然后通过 PermissionRefresher 通知 Shiro 重新读权限。具体来说,就是重写了 Shiro 里 AuthorizingRealm 类的 doGetAuthorizationInfo 方法,每次检查权限时都从 Redis 拿最新的,改完权限不用重启,立马能用。商家中午改了客服的权限,下午客服登录就有新权限了,方便得很。
URL 级别的权限控制,精细到每个接口。ZKmall 把所有 API 接口和前端路由按功能分组,比如 /api/merchant/product/* 都跟商品管理有关,每个 URL 都对应一个权限标识,像 "merchant:product:view"(看商品)、"merchant:product:add"(加商品)。Shiro 里有个 FilterChainDefinitionMap,专门存 URL 和权限的对应关系,用户访问某个接口时,Shiro 的 PermissionsAuthorizationFilter 就会自动查用户有没有对应的权限,没有就返回 403。对于 RESTful 接口,还能按 GET/POST 这些方法细分,比如 GET /api/merchant/product/\{id\} 需要 "view" 权限,PUT 方法就需要 "edit" 权限,管得特别细。
数据权限自动过滤,不用手动写条件。ZKmall 自己写了个 DataPermissionInterceptor 拦截器,插在 MyBatis 的执行流程里,执行 SQL 前自动加过滤条件。这个拦截器通过 Shiro 拿到当前登录用户的商户 ID,商家查数据时,SQL 里会自动加上 "merchant_id=xxx";平台管理员查数据时,就看他的权限范围,能看所有的就不加,只能看某类商户的就加上对应的条件。比如商家查自己的订单,实际执行的 SQL 会多一句 "AND merchant_id=1001",不用开发人员在每个 Mapper 里手动写,既省代码又不容易出错,不会因为忘了加条件导致数据泄露。
前端权限标签,没权限的按钮直接藏起来。前端 Vue 组件里有个自定义指令 v-permission,能控制按钮、菜单显不显示。比如一个 "新增商品" 按钮,加上 v-permission="'merchant:product:add'",用户没有这个权限,按钮就直接隐藏了。菜单权限也类似,前端路由配置里有个 meta: \{permission: 'xxx'\},路由守卫跳转前会检查权限,没权限就跳到 403 页面。这样前端藏起来,后端再校验一遍,既安全又友好,用户不会点了按钮才发现没权限。

多角色认证与会话管理:登录安全又灵活
多商户系统里,平台管理员、商家员工、买家的登录流程和会话管理都不一样,ZKmall 基于 Shiro 搞了套适配多角色的认证体系,登录安全,会话也管得好。
多角色登录流程,一个入口分身份。平台管理员和商家员工用同一个登录页,但账号格式不一样,比如商家员工的账号带商户前缀,像 "merchant_1001_zhang",1001 就是商户 ID。登录时,CustomUsernamePasswordToken 会带上用户类型,Shiro 的 AuthenticatingRealm 根据类型用不同的认证策略:平台管理员从 admin_user 表查,密码用 BCrypt 加密验证;商家员工从 merchant_user 表查,还得检查他所属的商家是不是正常运营,没被冻结。登录成功后,用户信息(ID、类型、商户 ID)会存在 Shiro 的 Subject 里,后面检查权限就靠它。"记住我" 功能也有,用 Shiro 的 RememberMeManager 把用户信息加密存在 Cookie 里,7 天内不用重新登录。
集成 JWT,适应微服务架构。微服务里用传统的 Session 认证不方便,多个服务之间不好共享会话。ZKmall 加了 JWT,登录成功后服务器生成一个包含用户信息和权限摘要的 JWT 令牌,返回给客户端。客户端后面每次请求都在 HTTP 头里带这个令牌,Shiro 的 JwtFilter 拦截到请求后,先验证令牌是不是有效,有效就解析出用户信息,创建 Subject 对象。JWT 令牌 2 小时过期,客户端会定时刷新,不用老登录。这种无状态的认证方式,服务可以随便加机器,适合微服务扩缩容。
会话并发控制,防止账号乱用。ZKmall 可以设商家员工最多能在几台设备登录,默认是 2 台,超过了就把最早登录的踢掉。这是通过 Shiro 的 SessionDAO 实现的,登录成功后查一下当前有多少个会话,超了就清掉旧的。平台管理员这种敏感账号,强制只能在一台设备登录,避免账号共享出问题。会话信息存在 Redis 里,多台服务器也能共享,还设了超时时间,30 分钟没操作就自动登出,安全又省资源。
密码安全策略,把账号锁得牢牢的。平台强制密码得 8 位以上,字母、数字、特殊字符都得有,用自定义的 PasswordValidator 检查。密码存的时候用 BCrypt 加密,这算法自带盐值,比 MD5 安全多了,破解难度大得多。还会提醒用户定期改密码,90 天不改就限制部分操作。登录异常也管,比如异地登录、短时间输错好几次密码,就要求输验证码,或者暂时锁 15 分钟,防止账号被盗。

权限管理功能:平台和商家都好用
权限管理得有好用的界面,平台管理员能统筹全局,商家能管好自己人。ZKmall 的权限管理功能做得挺直观,配置起来不费劲。
平台权限管理界面,全局权限一手掌控。平台管理员在 "系统管理 - 权限配置" 页面能看到所有权限,树形结构按模块分组,增删改都方便。"角色管理" 里可以建角色,比如 "商户审核员",然后在权限树上勾选权限分配给他。"用户管理" 里把平台用户和角色关联起来,一个用户可以有多个角色,权限取并集。给商家的权限也能预设模板,比如 "标准商户" 和 "旗舰商户" 权限不一样,新商家入驻时自动给对应的模板,不用一个个配,省事儿。
商户权限管理界面,自己的人自己管。商家管理员登录后,在 "店铺设置 - 角色管理" 里能看到的权限都是平台给的,看不到平台级别的权限。可以建自定义角色,比如 "库存专员",从自己有的权限里挑一部分给他。"员工管理" 里能加员工账号,分配不同角色,比如让客服只能处理订单咨询,不能改商品价格,各司其职。改了权限马上生效,员工不用重新登录,当场就能用新权限,管理效率高。
权限复制和迁移,批量操作省时间。要建个类似的角色,比如 "商户管理员 2" 和现有的 "商户管理员" 差不多,直接 "复制角色",再改改不一样的地方就行。商家权限模板能导出导入,开发、测试、生产环境之间同步配置方便得很。商家升级,比如从标准商户升到旗舰商户,平台执行 "权限迁移",在原来的基础上加点新权限,不用从头配。
权限审计日志,谁改了什么都记着。所有权限变更都有记录,建角色、改权限、给用户分配角色,都记着操作人、时间、改了啥。还能按条件查,出了问题能追溯。敏感权限,比如改订单金额的权限被改了,系统会自动通知负责人,心里有个数。日志存 180 天,符合合规要求。
安全加固与性能优化:权限系统也能扛高并发
多商户系统的权限模块用得特别频繁,还容易被攻击,ZKmall 做了不少优化,保证它又安全又能跑。
防越权加固,把漏洞堵死。除了常规的权限检查,还加了几道防线:接口参数里的商户 ID 得和登录用户的商户 ID 一致,比如商家改商品时,商品的 merchant_id 必须是他自己的;改管理员密码、分配核心权限时,得再输一遍当前密码或短信验证码;前端页面里,别家商户的敏感信息比如联系方式,直接隐藏掉。还定期扫描安全漏洞,模拟改商户 ID 这种越权攻击,看看防得住不。
缓存优化,权限检查飞得快。权限数据查得多、变得少,ZKmall 把它们存在 Redis 里,比如用户有哪些权限、角色有哪些权限、URL 对应什么权限,都缓存起来,有效期 24 小时。改了权限就主动清缓存,保证数据对。加了缓存后,权限检查从 50 毫秒降到 5 毫秒以内,数据库压力小多了。
权限粒度控制,平衡安全和性能。权限太细,比如每个按钮都单独控制,管理起来麻烦,检查起来也费时间;太粗,又满足不了需求。ZKmall 搞了 "模块 - 功能 - 操作" 三级粒度,大部分情况控制到功能级就行,比如 "商品管理",核心操作比如付款、退款才控制到操作级。这样既安全,权限数据量又不大,检查起来快。
集群环境适配,多服务器也一致。多台服务器部署时,Redis 集群保证缓存共享,Shiro 的会话存在 Redis 里,多台服务器都能着。改了权限,通过消息队列通知所有服务器刷新缓存,不管访问哪台服务器,权限都是最新的。这样系统能随便加机器,扛得住高并发。

实践经验:从入驻到退出全流程管到位
ZKmall 在多商户权限管理上踩过不少坑,总结了些经验,从商家入驻到退出都有一套办法。
商户入驻权限初始化,一键搞定。新商家审核通过后,系统自动给他分配权限模板,建个 "商户管理员" 角色,给全权限,再建个主账号关联这个角色。主账号登录后,自己建子账号和角色就行。以前手动配要几小时,现在几分钟就好,商家能快点开业。
大型商户多部门权限隔离,各司其职。连锁品牌这种大商家,部门多,采购、销售、客服各管一摊。ZKmall 支持按部门建角色,采购部有商品入库权限,销售部能改价格,客服只能处理订单。部门之间数据隔离,销售看不到采购成本,客服只能处理自己部门的订单。权限和数据权限结合,大商家内部也能管得明明白白。
临时权限和代运营,灵活又可控。商家搞促销,临时给员工 "限时折扣" 权限,到期自动收回,不用手动改。还能授权别人代运营,比如让代运营公司帮忙上下架商品,权限能精确到具体操作,想收回来随时收。这些操作都有记录,出了问题能查到。
商户退出权限回收,数据安全不留漏。商家被清退或自己退出,系统立马冻结所有账号,数据标为 "已冻结",只有平台能看。权限配置和操作日志至少存 1 年,方便审计。商家合并,比如 A 收购 B,能把 B 的权限和数据平滑迁到 A,业务不中断。
ZKmall 用 Shiro 搭的这套多商户权限体系,把动态权限、细粒度控制、数据隔离都做到了,解决了 B2B2C 商城的权限难题。平台能管好全局,商家有足够的自主权,性能和安全也都扛得住。对于开源商城来说,这不只是个技术方案,更是一套平衡平台管控和商家灵活运营的模式,能支撑商城越做越大。