Я проектирую очень простую систему RBAC (управление доступом на основе ролей) в своем проекте PHP, и, подумав над этим, я нашел решение, но, не зная много о построении бизнес-систем, я не уверен, что есть или были бы какие-то серьезные недостатки дизайна с моим решением.
В общем, я хочу дать пользователю набор «ролей». Я буду использовать эти роли, чтобы разрешить или запретить доступ к определенным функциям приложения.
Вот таблица ролей:
# 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
- 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 |
+-----------------------------------------------+
Здесь важно то, что вы не можете изменять записи в этих таблицах напрямую как администратор систем, поэтому нет графического интерфейса
добавлять / удалять действия и т. д.
Следующим шагом является создание некоторых ролей для нашей системы, это делается через пользовательский интерфейс администратора, роли могут быть определены произвольно.
Давайте начнем с некоторых основных ролей:
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 |
+-----------------------+
В нашем супер причудливом пользовательском интерфейсе администратора теперь есть боковая панель со списком всех установленных модулей и связанных с ними действий. поскольку
наш типичный администратор супер ленив и ничего не знает о программировании, теперь он может удобно назначать действия каждому
роль с перетаскиванием&падение, т.е. назначение разрешений ролям.
Эти назначенные разрешения хранятся в нашей таблице сопоставления. 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) |
+---------------------------------+
Таким образом, один пользователь может иметь несколько ролей, и разрешения (пары модуль / действие) затем объединяются на уровне приложения.
Если вы хотите сделать еще один шаг вперед, вы также можете предоставить своего рода наследование в образце для подражания, например, Q&Модератор
может быть определен как дочерний элемент Q&Пользователь, унаследовавший все разрешения от Q&Пользователь и расширение его правами модератора.
Так как же может выглядеть авторизация на прикладном уровне?
Давайте предположим, что Дилберт, который является Q&Модератор и модератор чата входят в систему, затем вы собираете все свои роли и все
разрешения, назначенные этим ролям. Далее вы начинаете строить дерево разрешений и удалять все дублирующиеся разрешения, разрешения
Дерево может быть представлено в виде ассоциативного массива, например:
Дерево разрешений Дилберта:
$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]));
}
В нашем коде теперь мы можем проверить, разрешено ли Дилберту делать определенные вещи с помощью синтаксиса:
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
Других решений пока нет …