我的基于角色的访问控制是否可行?

Jam*_*ery 6 php rbac

我在我的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
Run Code Online (Sandbox Code Playgroud)

我担心的问题是数据库中没有关于角色实际做什么的信息.但后来我开始思考这是否重要.因为如果角色具有有意义的名称和结构,那么您可以查询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
}
Run Code Online (Sandbox Code Playgroud)

角色可以被管理,并且可以在组织中具有他们喜欢的任何含义.我只是担心数据库中的角色没有任何意义,我不禁想到可能有更好的方法来实现这一点.

我的解决方案长期可行吗?

谢谢

sle*_*led 25

这是我对这种RBAC系统的方法,首先我将应用程序拆分为模块,至少在逻辑上.

将模块视为应用程序中可以对其执行某些操作的实体/资源.资源可以是用户,成员资格,产品,......

我们假设我们正在构建一个类似Stackoverflow的站点,我们有以下模块:

  • 问题(问答部分)

这些模块中的每一个都在应用程序数据库表中注册,它们modules看起来像:

# Modules
  - id [an application-wide unique module identifier, provided by the module]
  - name [string, human readable]
Run Code Online (Sandbox Code Playgroud)

这些模块中的每一个都带有一组由模块预先定义的动作,例如创建问题的动作,从聊天室踢出用户等等.这些动作与模块一起安装到称为"动作"的应用程序数据库表中,看起来像:

# 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]
Run Code Online (Sandbox Code Playgroud)

现在让我们为Stackoverflow克隆定义模块和操作:

Modules
+------------------------------------------+
| ID        | Name                         |
+------------------------------------------+
| questions | Question and Answer Module   |
| chat      | Chat Module                  |
+------------------------------------------+
Run Code Online (Sandbox Code Playgroud)

与模块一起,将安装操作:

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      |
+-----------------------------------------------+  
Run Code Online (Sandbox Code Playgroud)

这里重要的是你不能直接修改这些表中的条目作为系统的管理员,所以没有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
Run Code Online (Sandbox Code Playgroud)

首先,角色在角色表中创建:

# Roles
    - id [auto-increment, unsigned]
    - name [string, length:50]
Run Code Online (Sandbox Code Playgroud)

填充我们的自定义定义:

Roles
+-----------------------+
| ID | Name             |
+-----------------------+
| 1  | Q&A User         |
| 2  | Q&A Moderator    |
| 3  | Q&A Admin        |
| 4  | Chat User        |
| 5  | Chat Moderator   |
| 6  | Chat Admin       |
+-----------------------+
Run Code Online (Sandbox Code Playgroud)

在我们超级精彩的管理界面中,我们现在有一个侧边栏,其中包含所有已安装模块及其相关操作的列表.由于我们的典型管理员非常懒,并且对编程一无所知,现在他可以通过拖放方便地为每个角色分配操作,即为角色分配权限.

这些分配的权限存储在我们的映射表中Roles_Actions:

# Roles_Actions
  - role_id
  - module_id 
  - action

  PK: [role_id, module_id, action]
Run Code Online (Sandbox Code Playgroud)

填充表:

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  |
+--------------------------------+
Run Code Online (Sandbox Code Playgroud)

现在我们必须将角色分配给系统中的用户,假设我们最初有四个用户:

- 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]
Run Code Online (Sandbox Code Playgroud)

填充表:

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) | 
+---------------------------------+
Run Code Online (Sandbox Code Playgroud)

因此,一个用户可以拥有多个角色,然后在应用程序层中将权限(模块/操作对)合并在一起.如果您想更进一步,您还可以在角色模型中提供某种继承,例如,可以将Q&A主持人定义为问答用户的子项,继承Q&A用户的所有权限并使用主持人权限进行扩展.

那么授权在应用层上怎么样呢?

让我们假设Dilbert是一个Q&A主持人和聊天主持人登录系统,然后你收集他的所有角色和分配给这些角色的所有权限.接下来,您开始构建权限树并删除所有重复的权限,权限树可以表示为关联数组,如:

Dilbert的许可树:

$dilberts_permissions = array(
   "questions" => array("read", "create", "vote", "edit")
   "chat"      => array("join", "kick")
)
Run Code Online (Sandbox Code Playgroud)

为方便起见,我们创建了一个简单的帮助函数:

function has_permission($path, $tree) {
   list($module, $action) = explode('.', $path);
   return (array_key_exists($module, $tree) && in_array($action, $tree[$module]));
}
Run Code Online (Sandbox Code Playgroud)

在我们的代码中,我们现在可以检查是否允许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
Run Code Online (Sandbox Code Playgroud)

  • 很好的答案!我想知道的一件事是,如果您不想让聊天主持人读取所有聊天内容,这将如何工作。比如我想让 Dilbert 做一个聊天管理员,但仅限于他自己创建的聊天室。在 Chuck 创建的房间中,Dilbert 是主持人。 (4认同)