用户角色权限系统设计:RBAC最佳实践
2024-12-26 16:46:20
用户角色与权限系统设计最佳实践
角色与权限管理是应用程序开发中的常见需求。它关乎系统的安全性与灵活性,糟糕的设计会为维护与扩展带来巨大挑战。
权限控制模型选择
权限控制模型并非只有一种。 常用的包括:基于角色的访问控制(RBAC), 基于属性的访问控制(ABAC), 以及访问控制列表(ACL) 。 这里我们将关注RBAC, 因为它在大多数web应用中具有良好表现,也相对简单易懂。RBAC将用户与角色关联,角色绑定权限。
原因分析 : 直接基于用户绑定权限不利于维护;如果一个用户的权限发生更改,需要更改多个地方。使用角色管理可以将用户的权限变化归于角色变化,将权限修改的影响最小化,同时方便权限集中管理。
方案 : 采用RBAC模型,为用户定义不同的角色。例如,管理员
,编辑
,访客
等,不同角色拥有不同操作权限。
数据模型设计
明确模型是成功的第一步。在关系型数据库(如MySQL)中,常见做法是建立如下几张表:
-
users
表: 存储用户信息CREATE TABLE users ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(255) NOT NULL UNIQUE, password VARCHAR(255) NOT NULL, email VARCHAR(255) UNIQUE, group_id INT, -- 其他用户字段 FOREIGN KEY (group_id) REFERENCES groups(id) -- 假设存在 groups 表 );
-
roles
表:存储角色信息CREATE TABLE roles ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL UNIQUE );
-
groups
表: 存储用户组信息CREATE TABLE groups ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL UNIQUE, parent_id INT NULL, FOREIGN KEY (parent_id) REFERENCES groups(id) -- 自引用表示子组, 可以为空。 );
-
permissions
表: 存储权限信息CREATE TABLE permissions ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL UNIQUE );
-
role_permissions
表: 存储角色与权限的对应关系CREATE TABLE role_permissions ( role_id INT, permission_id INT, PRIMARY KEY (role_id, permission_id), FOREIGN KEY (role_id) REFERENCES roles(id), FOREIGN KEY (permission_id) REFERENCES permissions(id) );
-
user_roles
表: 存储用户与角色的对应关系CREATE TABLE user_roles ( user_id INT, role_id INT, PRIMARY KEY (user_id, role_id), FOREIGN KEY (user_id) REFERENCES users(id), FOREIGN KEY (role_id) REFERENCES roles(id) );
-
group_permissions
表: 存储组与权限的对应关系CREATE TABLE group_permissions ( group_id INT, permission_id INT, PRIMARY KEY (group_id, permission_id), FOREIGN KEY (group_id) REFERENCES groups(id), FOREIGN KEY (permission_id) REFERENCES permissions(id) );
-
content_permissions
表: 存储特定内容访问权限. 这张表允许更精细的内容级别控制。
CREATE TABLE content_permissions (
content_id INT, -- 关联具体内容的id, 依据不同的内容表修改
permission_id INT, -- 特定操作的权限,对应`permissions`表
group_id INT NULL, -- 允许的组
user_id INT NULL, --允许的特定用户。 group_id 和 user_id至少有一个值, 或者都可以为空代表公开访问.
PRIMARY KEY(content_id, permission_id, group_id,user_id ),
FOREIGN KEY(permission_id) REFERENCES permissions(id),
FOREIGN KEY(group_id) REFERENCES groups(id),
FOREIGN KEY(user_id) REFERENCES users(id)
);
方案解读 :
users
表: 用户组(group_id),这允许进行用户分组管理,可以简化权限分配(权限继承)。roles
表: 管理系统内各种角色名称,比如:管理员,编辑者,查看者等。groups
表: 构建组织机构树。允许嵌套组。permissions
表 : 管理具体的操作权限名称, 如:create_post
,edit_post
,delete_post
,read_user_list
,create_user
,edit_user
等。role_permissions
表 : 表明不同角色拥有的操作权限。方便不同角色复用权限。user_roles
表 : 将用户和角色进行绑定。 一个用户可以拥有多个角色。group_permissions
表 : 为用户组分配角色,同理方便了权限复用和管理content_permissions
表: 将特定的权限附加到具体内容上, 它可以针对某个用户或者某个用户组开放内容。为具体内容进行权限配置, 让控制粒度更精细。
具体场景解决方案
问题中的需求可以用上面的数据模型解决:
- 根用户 (root user) : 创建角色为"超级管理员(super admin)"的用户, 将所有权限赋给此角色,根用户可以创建新的子根用户,并把其他权限分配给子根用户. 子根用户可以是同组或者不同组的用户。
- 子根用户 : 赋予子根用户"管理用户(manage_user)" 权限, 创建用户时将新用户添加进自己的用户组。 为组设定
group_permissions
,或者设置该组角色。 这样组内用户就可以拥有该组权限。 - 用户权限 : 基于用户所属的
group
和赋予的角色权限,查询用户的权限。对于每个需要进行权限验证的操作, 可以检查该用户的user_roles
和用户所属的groups
和group_permissions
来验证用户是否具有对应的操作权限。通过结合内容表和content_permissions
表可以实现更精细化的内容权限控制, 根据内容的属性如(所有者、创建者所属的用户组),以及内容绑定的用户和组的权限规则来实现不同的权限组合。
代码示例 (PHP)
以下是权限验证示例, 需要依赖ORM(Doctrine or Eloquent). 如果未使用ORM需要自行实现SQL查询.
<?php
// 假设已获取当前用户 $user 和需要检查的权限 $permission_name
// 根据用户 id 获取用户角色
function getUserRoles(int $user_id) : array {
// UserRoles 对应 `user_roles` table 对应的 ORM model
$userRoles = UserRoles::where("user_id", $user_id)->with('role')->get()->toArray();
$roles = [];
foreach($userRoles as $ur) {
$roles[] = $ur['role']['name'];
}
return $roles;
}
//根据 role_id 获得拥有的所有 permission 的 id
function getRolePermissions (array $roles) : array{
//Role 对应 `roles` table 的 ORM model, `permission` 是`permissions`的ORM model
//role_permissions 对应 `role_permissions` table 的ORM model
$allPermissions = [];
foreach ($roles as $role){
$r = Role::where("name",$role)->with(['permissions'])->first();
foreach($r['permissions'] as $p){
$allPermissions[]= $p->name;
}
}
return array_unique($allPermissions);
}
// 根据用户的 `group_id` 获取 group 的permission ids.
function getGroupPermissions(int $group_id) : array{
$groupPermissions = GroupPermissions::where("group_id",$group_id)->with("permission")->get()->toArray();
$permissionNames = [];
foreach($groupPermissions as $gp){
$permissionNames[] = $gp['permission']['name'];
}
return array_unique($permissionNames);
}
function checkUserPermission(User $user, string $permissionName) : bool
{
// 获取用户的 role name
$roles = getUserRoles($user->id);
// 根据用户roles, 获取 permission list
$userPermissions = getRolePermissions($roles);
// 获得group permission
$groupPermissions = getGroupPermissions($user->group_id);
$finalPermissions = array_merge($userPermissions, $groupPermissions);
return in_array($permissionName, $finalPermissions);
}
// 在访问资源或者执行操作时,调用检查用户权限
$canEditPost = checkUserPermission($user, 'edit_post');
if($canEditPost) {
echo "可以编辑文章.";
}
额外建议:
- 密码哈希存储:避免明文存储密码, 使用 bcrypt 或 Argon2 等强哈希算法。
- 定期审计: 定期审查用户角色、权限分配,及时删除过期权限。
- 权限最小化原则: 仅赋予用户所需的最低权限。
- 防止SQL注入:确保使用参数化查询来防止SQL注入攻击。
- 输入验证: 对用户输入进行校验。
设计良好的角色与权限系统对于应用的长期健康发展至关重要。一个清晰的结构将使维护更加简单, 也降低错误风险,最终提升用户体验。