我正在我的PHP项目中设计一个非常简单的RBAC(基于角色的访问控制)系统,经过一番思考,我想出了一个解决方案,但是由于对构建业务系统知之甚少,我不确定我的解决方案是否存在或会有任何重大设计缺陷。
所以基本上,我想给用户一组"角色"。我将使用这些角色来允许或拒绝对应用程序上某些功能的访问。
以下是角色表:
# Roles
- id [auto-increment, unsigned]
- role [string, length:50]
# User_Roles
- user_id [FK:user_id, unsigned]
- role_id [FK:roles_id, unsigned]
Note: user_id and role_id to be unique index
我担心的问题是数据库中没有关于角色实际工作的信息。但后来我开始思考这是否重要。因为如果角色具有有意义的名称和结构,则可以查询user_roles
表以获取所有用户角色,然后在代码中如下所示:
# Fetch user with ID 1 from database
$user = User::find(1);
# Fetch the roles the user has from the database
# @returns : Array of roles
$userRoles = $user->roles()
# $userRoles = ['newmember', 'member.post', 'member.chat']
# Can the user send a message?
if(in_array('member.message', $userRoles)
{
# User can send a message
}
else
{
# User can not send a message
}
这些角色是可以管理的,并且可以在组织内具有他们喜欢的任何含义。我只是担心数据库中的角色没有意义,我不禁认为可能有更好的方法来实现这一目标。
我的解决方案长期可行吗?
谢谢
这是我对此类 RBAC 系统的方法,首先我将应用程序拆分为模块,至少在逻辑上是这样。
将模块视为应用程序中的实体/资源,可以对其执行某些操作。资源可以是用户、成员资格、产品等。
假设我们正在构建一个像 Stackoverflow 这样的站点,我们有以下模块:
- 问题(问答部分)
- 聊天
这些模块中的每一个都在名为 modules
的应用程序数据库表中注册自己,如下所示:
# Modules
- id [an application-wide unique module identifier, provided by the module]
- name [string, human readable]
这些模块中的每一个都带有一组由模块预定义的操作,例如创建问题、将用户踢出聊天室等的操作。这些操作与模块一起安装到名为"actions"的应用程序数据库表中,如下所示:
# Actions
- module_id [reference to the modules table, is the namespace for the token]
- action [string / int, action identifier provided by the module, unique in the scope of the module]
- name [string, human readable name]
- Primary Key: [module_id, action]
现在让我们定义 Stackoverflow 克隆的模块和操作:
Modules
+------------------------------------------+
| ID | Name |
+------------------------------------------+
| questions | Question and Answer Module |
| chat | Chat Module |
+------------------------------------------+
与模块一起,将安装操作:
Actions
+-----------------------------------------------+
| Module | Action | Name |
+-----------------------------------------------+
| questions | read | Read Questions |
| questions | create | Create Questions |
| questions | edit | Edit Questions |
| questions | delete | Delete Questions |
| questions | vote | Vote on Questions |
| | | |
| chat | join | Join the Chat |
| chat | kick | Kick users |
| chat | create | Create Chatrooms |
+-----------------------------------------------+
这里重要的是,您不能以系统管理员的身份直接修改这些表中的条目,因此没有 GUI添加/删除操作等。
下一步是为我们的系统创建一些角色,这是通过管理员的用户界面完成的,角色可以任意定义。
让我们从一些基本角色开始:
Q&A User:
- can read questions
- can create questions
Q&A Moderator:
- can read questions
- can create questions
- can vote on questiosn
- can edit questions
Q&A Admin:
- can read questions
- can create questions
- can vote on questiosn
- can edit questions
- can delete questions
Chat User:
- can join the chat
Chat Moderator:
- can join the chat
- can kick users from the chat
Chat Admin:
- can join the chat
- can kick users from the chat
- can create chat rooms
首先,在角色表中创建角色:
# Roles
- id [auto-increment, unsigned]
- name [string, length:50]
填充了我们的自定义定义:
Roles
+-----------------------+
| ID | Name |
+-----------------------+
| 1 | Q&A User |
| 2 | Q&A Moderator |
| 3 | Q&A Admin |
| 4 | Chat User |
| 5 | Chat Moderator |
| 6 | Chat Admin |
+-----------------------+
在我们超级花哨的管理UI中,我们现在有一个侧边栏,其中包含所有已安装模块及其相关操作的列表。因为我们典型的管理员超级懒惰,对编程一无所知,他现在可以方便地为每个分配操作带有拖放功能的角色,即为角色分配权限。
这些分配的权限存储在我们的映射表中,Roles_Actions
:
# Roles_Actions
- role_id
- module_id
- action
PK: [role_id, module_id, action]
填充的表:
Roles_Actions
+--------------------------------+
| Role ID | Module ID | Action |
+--------------------------------+
| 1 | questions | read |
| 1 | questions | create |
| 2 | questions | read |
| 2 | questions | create |
| 2 | questions | vote |
| 2 | questions | edit |
...
| 6 | chat | join |
| 6 | chat | kick |
| 6 | chat | create |
+--------------------------------+
现在我们必须将角色分配给系统中的用户,假设我们最初有四个用户:
- Chuck Norris which is a Q&A Admin and also a Chat Admin (UID = 1)
- Bruce Willis which is a Q&A Moderator and a Chat User (UID = 2)
- Dilbert which is a Q&A Moderator and a Chat Moderator (UID = 3)
# User_Roles
- user_id [FK:user_id, unsigned]
- role_id [FK:roles_id, unsigned]
填充的表:
Users_Roles
+---------------------------------+
| User ID | Role ID |
+---------------------------------+
| 1 | 3 (= Q&A Admin) |
| 1 | 6 (= Chat Admin) |
| 2 | 2 (= Q&A Moderator) |
| 2 | 4 (= Chat User) |
| 3 | 2 (= Q&A Moderator) |
| 3 | 5 (= Chat Moderator) |
+---------------------------------+
因此,一个用户可以有多个角色,然后将权限(模块/操作对)合并到应用程序层中。如果你想更进一步,你也可以在榜样中提供某种继承,例如问答主持人。可以定义为问答用户的子级,继承问答用户的所有权限,并使用版主权限对其进行扩展。
那么授权在应用层上会是什么样子呢?
让我们假设Dilbert是问答主持人和聊天主持人登录系统,然后你收集他的所有角色和所有分配给这些角色的权限。接下来,您开始构建权限树并删除所有重复的权限,即权限树可以表示为关联数组,如下所示:
呆伯特的权限树:
$dilberts_permissions = array(
"questions" => array("read", "create", "vote", "edit")
"chat" => array("join", "kick")
)
为了方便起见,我们创建了一个简单的辅助函数,如下所示:
function has_permission($path, $tree) {
list($module, $action) = explode('.', $path);
return (array_key_exists($module, $tree) && in_array($action, $tree[$module]));
}
在我们的代码中,我们现在可以检查是否允许 Dilbert 使用语法执行某些操作,例如:
has_permission("questions.create", $dilberts_permissions); // => TRUE
has_permission("questions.delete", $dilberts_permissions); // => FALSE
has_permission("chat.join", $dilberts_permissions); // => TRUE
has_permission("chat.create", $dilberts_permissions); // => FALSE