不够。仅存角色id易导致权限逻辑分散、多角色支持缺失及策略扩展困难,应存预计算的权限数组$_session['permissions'],并配rbac五表结构与层级匹配校验函数。

PHP 中用 $_SESSION 存角色 ID 就够了吗?
不够。只存 $role_id 或 $user_role 是最常见也最危险的起点——它把权限判断逻辑全推到每次请求时手动写 if ($role === 'admin'),漏一处就开一个后门。
真实场景里,用户可能同时属于多个角色(如“编辑”+“审核员”),某功能还需叠加“部门白名单”或“数据范围限制”,硬编码角色名根本撑不住。
- 必须把「能做什么」提前算好,存在
$_SESSION['permissions']里,值是数组,例如['post:publish', 'user:read:own'] - 初始化权限不能在登录成功后临时拼接,要调一次数据库查出该用户所有有效权限(含角色继承、显式授权、部门策略等)
- 别用
serialize()存整个对象——反序列化风险高,且 PHP 升级后容易失效;纯字符串数组最稳
怎么判断「用户能否访问 /api/v1/orders/export」?
不是查角色名,也不是查菜单表里的 is_enabled 字段,而是比对权限字符串是否匹配当前路由所需的资源动作对。
比如你定义接口需要 order:export 权限,那就要检查 in_array('order:export', $_SESSION['permissions'])。但注意:这里容易错在两点。
立即学习“PHP免费学习笔记(深入)”;
- 路由路径和权限标识不一致:前端写
/api/v1/orders/export,后端权限却叫export_orders,中间没映射层就会断掉 - 没做前缀通配:管理员需要
order:*,但in_array('order:*', $perms)不会自动匹配order:export;得自己写个hasPermission('order:export')函数来支持*和:分隔的层级匹配 - 忽略 HTTP 方法差异:同个 URL,
GET /users可能只需user:read,但DELETE /users/123必须有user:delete;权限字符串里必须带动作动词
RBAC 数据库表最少要哪几张?
五张是底线,少一张就会被迫在代码里补逻辑漏洞。别信“三张表搞定 RBAC”的博客——那是 demo,不是生产系统。
-
users:存用户基础信息,关键字段是id、status(启用/禁用) -
roles:角色定义,如id=1, name='editor',不含权限 -
permissions:原子权限项,如id=101, code='post:publish', description='发布文章' -
role_permissions:角色与权限的多对多关系(不是角色与菜单!) -
user_roles:用户与角色的多对多关系(支持一人多角色)
漏掉 user_roles 就只能单角色;漏掉 role_permissions 就得在代码里硬编码角色权限;而把菜单、按钮、API 接口混在一张 permissions 表里,后期根本没法按粒度回收权限。
为什么中间件里用 include_once 加权限类总报错?
因为权限校验逻辑一旦涉及数据库查询,就必须确保它跑在框架的请求生命周期内——而不是被 include_once 塞进某个全局文件,在 autoload 之前就执行了。
更常见的坑是:你在入口文件 index.php 开了 session,但忘了在 CLI 脚本(如定时任务)里也初始化权限上下文,结果后台导出订单时报 Undefined index: permissions。
- 权限校验必须封装成函数或类方法,且明确依赖
$_SESSION和 DB 连接实例,不能有隐式全局变量 - CLI 环境下不要直接复用 Web 的 session 权限数组;改用配置驱动或独立 token 验证
- 别在
__autoload或spl_autoload_register里触发权限查询——类还没加载完,DB 连接可能都还没建好
权限不是开关,是上下文。它依赖用户状态、时间、IP、甚至当前请求的数据 ID。越想省事硬编码,后面 debug 越像在解谜题。











