本文目录导读:

- 基于位运算(Bitwise)—— 最简单的细粒度
- 基于 ACL(访问控制列表)—— 通用细粒度
- 基于 RBAC + 权限点(最常用)
- 基于 ABAC(属性级权限)—— 最强大、最复杂
- 使用 Casbin —— 生产级方案
- 总结建议
在 PHP 中实现细粒度权限(Fine-Grained Access Control),核心思路是不依赖固定的角色(如 admin/user),而是基于具体的“资源”和“操作”进行判断。
常见的实现方式有以下几种,按复杂度和适用场景排序:
基于位运算(Bitwise)—— 最简单的细粒度
适合权限数量极少(≤ 32/64 种)且固定不变的场景(如:增、删、改、查、导出)。
// 定义权限常量(2的n次幂)
define('PERM_VIEW', 1); // 0001
define('PERM_EDIT', 2); // 0010
define('PERM_DELETE', 4); // 0100
define('PERM_EXPORT', 8); // 1000
// 用户权限值(7 = 查看 + 编辑 + 删除)
$userPerm = 7; // 0111
// 检查是否有“删除”权限
if ($userPerm & PERM_DELETE) {
echo "可以删除";
}
优点:性能极高,数据库字段用一个 int 存储全部权限。
缺点:权限数量受限,无法动态扩展,不支持“某个部门的文档”这种属性级权限。
基于 ACL(访问控制列表)—— 通用细粒度
适合权限数较多、持续变化的场景,每个用户或角色直接关联一个权限列表。
数据库设计示例:
-- 权限表
CREATE TABLE permissions (
id INT PRIMARY KEY,
name VARCHAR(50), -- 'article.create'
description VARCHAR(255)
);
-- 用户-权限关联表
CREATE TABLE user_permissions (
user_id INT,
permission_id INT,
resource_id INT NULL, -- 可选:针对特定资源,NULL表示所有资源
PRIMARY KEY (user_id, permission_id, resource_id)
);
PHP 检查逻辑:
function checkPermission($userId, $permName, $resourceId = null) {
$sql = "SELECT 1 FROM user_permissions up
JOIN permissions p ON up.permission_id = p.id
WHERE up.user_id = ? AND p.name = ?
AND (up.resource_id = ? OR up.resource_id IS NULL)";
// 执行查询,返回 boolean
}
优点:灵活,可控制到每个具体资源(如只允许编辑某篇特定的文章)。
缺点:用户数量多时,数据量爆炸,管理困难。
基于 RBAC + 权限点(最常用)
适合大多数 Web 系统,结合角色解决用户数量大的问题,同时保留细粒度。
核心思想:角色包含权限,用户绑定角色,但支持针对某个用户额外添加/删除特定权限点。
数据库结构:
-- 权限点(最细粒度)
permissions: id, code('article.edit'), name
-- 角色
roles: id, name
-- 角色-权限关联
role_permissions: role_id, permission_id
-- 用户-角色关联
user_roles: user_id, role_id
-- 用户额外权限(覆盖角色)
user_extra_permissions: user_id, permission_id, type('allow'/'deny')
判断逻辑:
function canUser($userId, $permCode, $resourceContext = []) {
// 1. 查用户所在角色的权限集合
// 2. 查用户额外权限(deny优先于allow)
// 3. 合并后判断
// 4. 可加入自定义回调处理动态逻辑(如“只允许编辑自己创建的文章”)
}
提示:这种结构需要配合缓存(如 Redis 存储用户权限集合),否则每次判断都查库会很慢。
基于 ABAC(属性级权限)—— 最强大、最复杂
适合极度复杂的业务规则(如:财务总监可以审批金额 < 5000 的报销单,但金额 ≥ 5000 需总经理审批)。
核心概念:权限判断 = 用户属性 + 资源属性 + 环境属性(时间、IP…) + 操作 。
实现示例(伪代码):
function checkAccess($user, $resource, $action) {
// 规则引擎检查:
// if $user->role == '财务总监'
// AND $action == 'approve'
// AND $resource->amount < 5000
// THEN allow
}
常见实现方式:
- 自己写规则引擎(if-else / 表达式解析)
- 使用第三方包:如
php-casbin(推荐),支持 ACL/RBAC/ABAC 多种模型。
使用 Casbin —— 生产级方案
PHP-Casbin 是一个成熟的开源权限库,支持多种模型。
模型文件示例(RBAC + 资源控制):
[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [role_definition] g = _, _ [matchers] m = g(r.sub, p.sub) && keyMatch2(r.obj, p.obj) && regexMatch(r.act, p.act)
使用方式:
$e = new Enforcer('path/to/model.conf', 'path/to/policy.csv');
if ($e->enforce('alice', '/data/article/123', 'edit')) {
// 允许操作
}
总结建议
| 场景 | 推荐方案 |
|---|---|
| 权限很少且固定( ≤ 32 种) | 位运算 |
| 简单系统,用户少 | ACL |
| 中大型系统,标准 Web 应用 | RBAC + 权限点 |
| 复杂规则(多条件、动态) | ABAC / Casbin |
| 不想自己写 | Casbin 或使用框架自带(Laravel/ThinkPHP 的 Gate/Policy) |
最后提醒:权限检查一定在服务端后端做,前端隐藏按钮只是辅助体验,不可作为安全保障。