一、前言
在之前的系列文章中,我写了 LikeShop 的源码结构、分层架构、二开规范和安全加固。这一篇聚焦后台管理系统最基础也最容易在二开时踩坑的部分------菜单与权限体系。
做过二开的人应该都有过这种经历:新增了一个管理后台的功能页面,代码写完了,接口也调通了,但登录后台一看,菜单里根本找不到入口。或者菜单有了,但运营账号点进去提示"无权限"。这些问题的根源,都是没有理解 LikeShop 权限体系的表设计和配置逻辑。
LikeShop 的权限控制体系基于经典的 RBAC 模型设计,通过用户-角色-权限的三层关联结构实现权限管理。核心逻辑是:先创建角色并分配权限,再创建管理员账号并绑定角色。不同角色拥有不同的菜单访问权限和操作权限。
这篇文章就把这套体系的表结构、配置流程和常见坑拆开讲清楚。
二、RBAC 权限模型的三层架构
LikeShop 的权限模型可以用一句话概括:管理员绑定角色,角色拥有菜单权限,菜单决定可见入口和可调用的接口。
三层结构的关系如下:
ls_admin(管理员表)
│ role_id(外键)
▼
角色(角色表)
│ 权限关联
▼
菜单(菜单表)------ 决定可见菜单和可调用的接口权限
这个模型和很多后台系统的设计思路是一致的,但 LikeShop 在具体实现上有几个特点值得注意。
第一,菜单和权限节点是合并管理的。 在很多系统里,"菜单"和"权限节点"是两张独立的表------菜单管显示,权限节点管接口鉴权。但 LikeShop 把两者统一在菜单表中管理,一个菜单记录既决定了后台左侧导航栏是否显示这个入口,也决定了对应的接口是否有权限调用。这种设计的好处是配置更简单,不需要在两处分别维护;代价是权限粒度受菜单层级限制。
第二,权限校验在服务端强制执行。 前端隐藏菜单按钮只是"体验优化",不是安全措施。所有管理后台的接口必须通过权限校验中间件。官方在安全加固中明确:管理后台、商家权限做角色分级,接口增加路由权限校验,防止低权限账号越权操作高权限功能。
三、核心表结构解析
3.1 ls_admin 管理员表
ls_admin 是后台管理员的账号表,字段设计如下:
| 字段名 | 数据类型 | 注释 |
|---|---|---|
| id | int | 主键 |
| root | tinyint | 是否超级管理员:0-否;1-是 |
| name | varchar | 名称 |
| account | varchar | 账号 |
| password | varchar | 密码 |
| role_id | int | 角色id |
| multipoint_login | tinyint | 是否支持多处登录:1-是;0-否 |
| disable | tinyint | 是否禁用:0-否;1-是 |
| login_time | int | 最后登录时间 |
| login_ip | varchar | 最后登录ip |
| sid | int | 商户id |
几个关键字段的说明:
root 字段用于标识超级管理员。超级管理员不受角色权限限制,拥有所有菜单和接口的访问权限。二开时如果新增了权限节点,超级管理员默认就能访问,不需要额外配置。
role_id 是权限体系的核心关联字段,指向该管理员绑定的角色。一个管理员只能绑定一个角色------如果业务需要"一个人既是财务又是运营",需要新建一个同时包含财务和运营权限的角色,而不是给一个人绑定多个角色。
sid 是多商户版的商户标识。多商户版的 ls_admin 表同时存储了平台管理员和商户管理员的账号 ,通过 sid 字段区分:平台管理员的 sid 为 0,商户管理员的 sid 指向具体的商户。这意味着二开时查询管理员列表必须带上 sid 过滤条件,否则平台端会看到商户的管理员账号。
3.2 角色表
角色表存储角色的基本信息,关键字段包括:
| 字段名 | 注释 |
|---|---|
| id | 角色id |
| name | 角色名称 |
| desc | 角色描述 |
| auth_desc | 角色权限 |
auth_desc 是角色的权限描述字段,记录该角色被分配了哪些菜单权限。角色列表接口返回的数据中,auth_desc 字段就是从这个字段读取的。
多商户版的角色表需要区分平台级角色和商户级角色。 平台级角色(如"平台运营")可以管理所有商户,商户级角色(如"店铺运营")只能管理自己店铺的数据。这个区分通过角色的 sid 字段实现------和 ls_admin 一样,sid = 0 表示平台级角色,sid 非 0 表示商户级角色。
3.3 菜单表
菜单表是权限体系中最核心的表,存储了所有后台菜单和权限节点的定义。关键字段包括:
| 字段名 | 注释 |
|---|---|
| id | 菜单id |
| pid | 父级菜单id |
| name | 菜单名称 |
| path | 路由路径 |
| icon | 图标 |
| sort | 排序 |
| type | 类型(目录/菜单/按钮) |
| perms | 权限标识 |
pid 字段实现了菜单的树形结构。 pid = 0 表示顶级菜单,其他值指向父级菜单的 id。通过递归查询 pid,可以构建出完整的菜单树。
type 字段区分菜单类型。 通常有三种类型:目录(可展开的父级菜单)、菜单(具体的页面入口)、按钮(页面内的操作权限)。按钮类型的菜单不显示在左侧导航栏中,但会作为权限节点用于接口鉴权。
perms 字段是权限标识 ,通常用"模块:控制器:方法"的格式表示,比如 auth.admin:edit 对应编辑管理员接口。权限校验中间件会根据当前请求的路由,在菜单表中查找对应的 perms,然后检查当前用户的角色是否拥有这个菜单权限。
3.4 会话表 ls_admin_session
ls_admin_session 表管理管理员的登录会话:
| 字段名 | 注释 |
|---|---|
| admin_id | 用户id |
| terminal | 客户端类型:1-pc管理后台;2-mobile手机管理后台 |
| token | 令牌 |
| expire_time | 到期时间 |
| sid | 商户id |
管理员登录成功后,系统会生成一个 token 并写入这张表。后续请求的 token 校验就是通过查询这张表完成的。token 的过期时长配置在 server/config/project.php 的 admin_token 中,默认 8 小时。
四、菜单与权限的配置流程
理解了表结构之后,配置一个完整的权限体系需要按以下步骤操作。
4.1 第一步:添加菜单和权限节点
在管理后台的「权限管理」→「菜单」中,添加新的菜单或权限节点。这是整个配置流程的起点------没有菜单记录,后面的角色权限分配就无从谈起。
菜单权限的功能是设置后台菜单列表的新增、修改、删除,以及设置菜单权限。在配置时需要注意:
目录和菜单的区别。 目录(type=目录)是左侧导航栏中可展开的父级菜单,本身不对应页面;菜单(type=菜单)是具体的页面入口,点击后会打开对应的页面。
按钮权限的配置。 如果一个页面需要区分"可查看"和"可编辑"两种权限,需要为编辑操作单独添加一个按钮类型的权限节点。比如"管理员列表"页面,可以配置"查看管理员"和"编辑管理员"两个权限节点。
4.2 第二步:创建角色并分配权限
在「权限管理」→「角色」中创建角色。创建角色时需要填写角色名称和角色描述,然后勾选该角色可以访问的菜单权限。
官方的操作指引是:创建角色,不同角色不同权限。角色创建好之后再创建账号。
角色创建完成后,在角色列表中可以看到角色的名称、描述和权限信息。
4.3 第三步:创建管理员并绑定角色
在「权限管理」→「管理员」中创建管理员账号。创建时需要填写账号、名称、密码,并选择绑定的角色。
编辑管理员接口的参数中,role_id 是必填项,说明管理员必须绑定角色。同一个管理员也可以通过编辑接口重新分配角色。
4.4 第四步:验证权限
配置完成后,用新建的管理员账号登录后台,验证以下内容:
- 左侧导航栏是否只显示了该角色被授权的菜单
- 点击菜单后页面是否正常打开
- 页面内的操作按钮是否根据权限显示或隐藏
- 直接访问未授权的接口 URL 是否被拦截
五、二开时的常见坑与避坑指南
坑一:新增菜单后忘记分配权限
现象:新增了一个管理后台的菜单,超级管理员能看到,但普通运营账号登录后找不到这个菜单。
原因:菜单记录本身不携带权限信息,权限是通过角色分配的。新建的菜单需要手动勾选到对应角色的权限列表中。
解决:在「角色」编辑页面,重新勾选新增的菜单权限并保存。
坑二:按钮权限没有配置
现象:运营账号能看到"管理员列表"页面,但点击"编辑"按钮没有反应,或者提示"无权限"。
原因:只配置了"菜单"类型的权限节点,没有配置"按钮"类型的权限节点。页面能访问(菜单权限已授权),但编辑接口被拦截(按钮权限未授权)。
解决:在菜单管理中,为编辑操作添加一个按钮类型的权限节点,然后在角色中勾选这个权限节点。
坑三:多商户版查询管理员列表没有加 sid 过滤
现象:平台端的管理员列表中出现了商户的管理员账号,或者商户端看到了其他商户的管理员。
原因 :ls_admin 表同时存储平台管理员和商户管理员,通过 sid 字段区分。如果查询时没有加 sid 过滤条件,会把所有管理员都查出来。
解决 :平台端查询时加 sid = 0,商户端查询时加 sid = 当前商户id。
坑四:直接修改 ls_admin 的 role_id 绕过角色配置
现象 :为了方便,直接在数据库中把某个管理员的 role_id 改成另一个值,结果该管理员登录后权限异常。
原因 :角色切换不仅仅是改 role_id 字段,还需要清理该管理员的会话(ls_admin_session 中的 token)。如果不清理,管理员用旧 token 请求时,系统仍然按旧角色做权限判断。
解决 :修改管理员的角色后,让该管理员重新登录,或者手动清理 ls_admin_session 中该管理员的记录。
坑五:多商户版的权限节点注册到了错误的应用
现象:新增了一个商户端的管理接口,权限节点注册在了平台端的菜单中,导致商户账号无法访问。
原因:LikeShop 多商户版的管理后台分为平台端和商户端,两者的权限体系是独立的。平台端的菜单和商户端的菜单分别管理。
解决:明确新增的功能属于平台端还是商户端,将菜单权限节点注册到对应的应用模块中。
六、权限体系与接口鉴权的联动
理解菜单表如何与接口鉴权联动,是排查权限问题的关键。
6.1 鉴权流程
一次管理后台的接口请求,鉴权流程如下:
请求到达 → AuthMiddleware 拦截
↓
① 从 Header 中提取 token
↓
② 查询 ls_admin_session 验证 token 有效性
↓
③ 根据 token 找到对应的 admin_id
↓
④ 查询 ls_admin 获取 role_id
↓
⑤ 根据 role_id 查询该角色拥有的菜单权限
↓
⑥ 根据当前请求的路由,匹配菜单表中的 perms 字段
↓
⑦ 匹配成功 → 放行;匹配失败 → 返回无权限
这条链路中的任何一环出问题,都会导致权限异常。
6.2 常见权限异常排查
| 现象 | 排查点 |
|---|---|
| 登录后菜单不显示 | 角色是否勾选了对应菜单权限 |
| 菜单显示但页面报错 | 接口是否有权限访问(检查按钮权限节点) |
| 接口返回 403 | 当前路由是否在菜单表的 perms 中有对应记录 |
| 切换角色后权限没变 | 是否清理了旧的会话 token |
| 超级管理员权限受限 | ls_admin.root 字段是否为 1 |
七、总结
LikeShop 的后台权限体系基于 RBAC 模型,核心是三张表:
ls_admin 存储管理员账号,通过 role_id 绑定角色,通过 sid 区分平台和商户。
角色表 存储角色的权限分配信息,auth_desc 字段记录该角色拥有的菜单权限。
菜单表 存储所有菜单和权限节点,通过 pid 实现树形结构,通过 type 区分目录/菜单/按钮,通过 perms 字段与接口路由匹配。
配置流程是:添加菜单和权限节点 → 创建角色并分配权限 → 创建管理员并绑定角色 → 验证权限。
二开时最常见的坑是"新增菜单后忘记分配权限"和"按钮权限没有配置"。多商户版需要额外注意 sid 字段的过滤,避免平台端和商户端的数据泄露。
把这套体系的表结构和配置逻辑理解清楚,后台权限相关的问题就能快速定位和解决。