Управление привилегиями пользователя на основе ролей
Существуют различные подходы к управлению доступом к информации, каждый из них имеет свои плюсы и минусы. Например, использование битовой маски очень эффективно, но ограничено 32 или 64 уровнями (количеством бит в формате целого числа). А подход на основе списка доступа (ACL) позволяет назначать уровни доступа объектам, но с операциями могут возникнуть сложности.
В данной статье описывается реализация доступа на основе ролей (role based access control - RBAC). RBAC - модель системы разграничения прав доступа, в которой роли используются для выполнения различных задач, а разрешения на выполнение операций привязываются к ролям. Пользователь может получать одну или несколько ролей, которые ограничивают его полномочия на действия в системе.
Обратной стороной использования модели RBAC является то, что при плохом управлении ролями и разрешениями вся система доступа превращается в хаотичную массу. В быстро меняющейся бизнес среде может возникнуть необходимость в специальном операторе, который будет заниматься только отслеживанием назначений соответствующих ролей новым сотрудникам и удалений полномочий у тех, кто покинул компанию или сменил позицию в структуре. Определение новых ролей для возникающих рабочих мест, ревизия и удаление полномочий требуют постоянного контроля. Ошибка в управлении полномочиями может сделать брешь в системе безопасности.
Мы обсудим создание необходимых таблиц баз данных. Затем создадим два файла с классами: Role.php
- для выполнения определенных задач по управлению ролями, и PrivilegedUser.php
- для расширения класса пользователя. Затем приведем несколько примеров, как код может быть интегрирован в ваше приложение. Управление ролями и пользователями весьма похожи, поэтому у вас появится система для идентификации пользователей.
База данных
Нужно 4 таблицы для хранения ролей и информации о разрешениях: таблица roles
содержит ID и имя роли, таблица permissions
хранит ID и описание разрешения, таблица role_perm
содержит соответствие ролей и разрешений, а таблица user_role
содержит соответсвие ролей и пользователей.
С помощью такой схемы можно иметь неограниченное количество ролей и разрешений, а каждому пользователю может быть назначено любое число ролей.
Вот запрос CREATE TABLE
для создания структуры в базе данных:
CREATE TABLE roles ( role_id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, role_name VARCHAR(50) NOT NULL, PRIMARY KEY (role_id) ); CREATE TABLE permissions ( perm_id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, perm_desc VARCHAR(50) NOT NULL, PRIMARY KEY (perm_id) ); CREATE TABLE role_perm ( role_id INTEGER UNSIGNED NOT NULL, perm_id INTEGER UNSIGNED NOT NULL, FOREIGN KEY (role_id) REFERENCES roles(role_id), FOREIGN KEY (perm_id) REFERENCES permissions(perm_id) ); CREATE TABLE user_role ( user_id INTEGER UNSIGNED NOT NULL, role_id INTEGER UNSIGNED NOT NULL, FOREIGN KEY (user_id) REFERENCES users(user_id), FOREIGN KEY (role_id) REFERENCES roles(role_id) );
Обратите внимание, что последняя таблица user_role
ссылается на таблицу users
, которая определяется в другом месте. В нашем случае мы предполагаем, что user_id
является основным ключом в таблице users
.
Никаких изменений для таблицы users
не требуется, так как информация о правах доступа хранится отдельно. В отличие от других систем RBAC, в нашем случае пользователь не имеет никакой роли по умолчанию. Он просто не получает никаких полномочий. В классе PrivilegedUser
возможно сделать обнаружение пользователей без ролей и выдачу роли без полномочий в таком случае. Или также можно сделать запрос SQL для копирования ID пользователей и инициализации их нужной ролью по умолчанию.
Класс Role
Основная цель класса Role
- вернуть объект роли, который содержит список соответствующих полномочий. Таким образом можно просто проверить наличие нужных полномочий без дополнительных запросов SQL.
Вот код класса, который содержится в файле Role.php
:
<?php class Role { protected $permissions; protected function __construct() { $this->permissions = array(); } // Возвращаем объект роли с соответствующими полномочиями public static function getRolePerms($role_id) { $role = new Role(); $sql = "SELECT t2.perm_desc FROM role_perm as t1 JOIN permissions as t2 ON t1.perm_id = t2.perm_id WHERE t1.role_id = :role_id"; $sth = $GLOBALS["DB"]->prepare($sql); $sth->execute(array(":role_id" => $role_id)); while($row = $sth->fetch(PDO::FETCH_ASSOC)) { $role->permissions[$row["perm_desc"]] = true; } return $role; } // Проверка установленных полномочий public function hasPerm($permission) { return isset($this->permissions[$permission]); } }
Метод getRolePerms()
создает новый объект Role
на основе заданного ID роли. Затем использует выражение JOIN
для комбинации таблиц role_perm
и perm_desc
. Каждое разрешение, связанное с ролью, хранится в виде описания в качестве ключа и значения true. Метод hasPerm()
принимает описание разрешения и возвращает значение из текущего объекта.
Класс PrivilegedUser
С помощью нового класса, который дополняет существующий класс пользователя, можно использовать существующий код управления пользователями, дополняя его новыми методами для работы с привилегиями.
Код класса в файле PrivilegedUser.php
:
<?php class PrivilegedUser extends User { private $roles; public function __construct() { parent::__construct(); } // Изменяем метод класса User public static function getByUsername($username) { $sql = "SELECT * FROM users WHERE username = :username"; $sth = $GLOBALS["DB"]->prepare($sql); $sth->execute(array(":username" => $username)); $result = $sth->fetchAll(); if (!empty($result)) { $privUser = new PrivilegedUser(); $privUser->user_id = $result[0]["user_id"]; $privUser->username = $username; $privUser->password = $result[0]["password"]; $privUser->email_addr = $result[0]["email_addr"]; $privUser->initRoles(); return $privUser; } else { return false; } } // Наполняем объект roles соответствующими разрешениями protected function initRoles() { $this->roles = array(); $sql = "SELECT t1.role_id, t2.role_name FROM user_role as t1 JOIN roles as t2 ON t1.role_id = t2.role_id WHERE t1.user_id = :user_id"; $sth = $GLOBALS["DB"]->prepare($sql); $sth->execute(array(":user_id" => $this->user_id)); while($row = $sth->fetch(PDO::FETCH_ASSOC)) { $this->roles[$row["role_name"]] = Role::getRolePerms($row["role_id"]); } } // Проверяем, обладет ли пользователь нужными разрешениями public function hasPrivilege($perm) { foreach ($this->roles as $role) { if ($role->hasPerm($perm)) { return true; } } return false; } }
Метод getByUsername()
возвращает объект, заполненный информацией о соответствующем пользователе. Метод почти идентичен тому, что обычно используется в класса пользователей, но его нужно изменить, чтобы добавить объект для ролей.
Метод initRoles()
использует выражение JOIN
для комбинирования таблиц user_role
и roles
для сбора информации о ролях для текущего пользователя. Каждая роль наполняется соответствующими разрешениями с помощью метода класса Role
Role::getRolePerms()
.
Метод hasPrivilege()
получает описание разрешения и возвращает результат проверки его наличия у текущего пользователя.
С помощью описанных двух классов проверка привилегий пользователя будет выполняться очень просто:
<?php require_once "Role.php"; require_once "PrivilegedUser.php"; // Соединяемся с базой данных... // ... session_start(); if (isset($_SESSION["loggedin"])) { $u = PrivilegedUser::getByUsername($_SESSION["loggedin"]); } if ($u->hasPrivilege("thisPermission")) { // выполняем действие }
Здесь имя пользователя хранится в активной сессии, и для него создается новый объект PrivilegedUser
, для которого вызывается метод hasPrivilege()
. В зависимости от информации, хранящейся в базе данных, объект может выглядеть следующим образом:
object(PrivilegedUser)#3 (2) { ["roles":"PrivilegedUser":private]=> array(1) { ["Admin"]=> object(Role)#5 (1) { ["permissions":protected]=> array(4) { ["addUser"]=>bool(true) ["editUser"]=>bool(true) ["deleteUser"]=>bool(true) ["editRoles"]=>bool(true) } } } ["fields":"User":private]=> array(4) { ["user_id"]=>string(1) "2" ["username"]=>string(7) "mpsinas" ["password"]=>bool(false) ["email_addr"]=>string(0) "" } }
Организационные вопросы
Одним из преимуществ использования ООП для RBAC является возможность простого разделения логики и сервисных функций. Например, можно добавить следующие методы к классу Role
для управления ролями (добавление, удаление, и так далее):
// Вставить новую роль public static function insertRole($role_name) { $sql = "INSERT INTO roles (role_name) VALUES (:role_name)"; $sth = $GLOBALS["DB"]->prepare($sql); return $sth->execute(array(":role_name" => $role_name)); } // Вставить массив ролей для заданного ID пользователя public static function insertUserRoles($user_id, $roles) { $sql = "INSERT INTO user_role (user_id, role_id) VALUES (:user_id, :role_id)"; $sth = $GLOBALS["DB"]->prepare($sql); $sth->bindParam(":user_id", $user_id, PDO::PARAM_STR); $sth->bindParam(":role_id", $role_id, PDO::PARAM_INT); foreach ($roles as $role_id) { $sth->execute(); } return true; } // удалить массив ролей и все связи public static function deleteRoles($roles) { $sql = "DELETE t1, t2, t3 FROM roles as t1 JOIN user_role as t2 on t1.role_id = t2.role_id JOIN role_perm as t3 on t1.role_id = t3.role_id WHERE t1.role_id = :role_id"; $sth = $GLOBALS["DB"]->prepare($sql); $sth->bindParam(":role_id", $role_id, PDO::PARAM_INT); foreach ($roles as $role_id) { $sth->execute(); } return true; } // Удалить все роли для заданного пользователя public static function deleteUserRoles($user_id) { $sql = "DELETE FROM user_role WHERE user_id = :user_id"; $sth = $GLOBALS["DB"]->prepare($sql); return $sth->execute(array(":user_id" => $user_id)); }
Также можно добавить подобные методы для класса PrivilegedUser
:
// Проверка, обладает ли пользователь заданной ролью public function hasRole($role_name) { return isset($this->roles[$role_name]); } // Вставляем новое разрешение в роль public static function insertPerm($role_id, $perm_id) { $sql = "INSERT INTO role_perm (role_id, perm_id) VALUES (:role_id, :perm_id)"; $sth = $GLOBALS["DB"]->prepare($sql); return $sth->execute(array(":role_id" => $role_id, ":perm_id" => $perm_id)); } // Удаляем все разрешения из роли public static function deletePerms() { $sql = "TRUNCATE role_perm"; $sth = $GLOBALS["DB"]->prepare($sql); return $sth->execute(); }
Разрешения очень тесно связаны с логикой приложения, поэтому они требуют достаточно сложных процедур добавления и удаления. А роли можно легко модифицировать, создавать и удалять. с помощью интерфейса администратора.
Заключение
В данном уроке мы рассмотрели базу для системы управления доступом на основе использования ролей. Достаточно простой код позволяет построить гибкую масштабируемую систему разграничения прав пользователей. Однако должно внимание надо уделять управлению пользовательским полномочиями на этапе использования, иначе гибкость может привести к хаосу и неразберихе.
Источник: http://feedproxy.google.com/~r/ruseller/CdHX/~3/qtZVQdn1vhs/lessons.php
Дайджест новых статей по интернет-маркетингу на ваш email
Новые статьи и публикации
- 2024-11-26 » Капитан грузового судна, или Как начать использовать Docker в своих проектах
- 2024-11-26 » Обеспечение безопасности ваших веб-приложений с помощью PHP OOP и PDO
- 2024-11-22 » Ошибки в Яндекс Вебмастере: как найти и исправить
- 2024-11-22 » Ошибки в Яндекс Вебмастере: как найти и исправить
- 2024-11-15 » Перенос сайта на WordPress с одного домена на другой
- 2024-11-08 » OSPanel 6: быстрый старт
- 2024-11-08 » Как установить PhpMyAdmin в Open Server Panel
- 2024-09-30 » Как быстро запустить Laravel на Windows
- 2024-09-25 » Next.js
- 2024-09-05 » OpenAI рассказал, как запретить ChatGPT использовать содержимое сайта для обучения
- 2024-08-28 » Чек-лист: как увеличить конверсию интернет-магазина на примере спортпита
- 2024-08-01 » WebSocket
- 2024-07-26 » Интеграция с Яндекс Еда
- 2024-07-26 » Интеграция с Эквайринг
- 2024-07-26 » Интеграция с СДЕК
- 2024-07-26 » Интеграция с Битрикс-24
- 2024-07-26 » Интеграция с Travelline
- 2024-07-26 » Интеграция с Iiko
- 2024-07-26 » Интеграция с Delivery Club
- 2024-07-26 » Интеграция с CRM
- 2024-07-26 » Интеграция с 1C-Бухгалтерия
- 2024-07-24 » Что такое сторителлинг: техники и примеры
- 2024-07-17 » Ошибка 404: что это такое и как ее использовать для бизнеса
- 2024-07-03 » Размещайте прайс-листы на FarPost.ru и продавайте товары быстро и выгодно
- 2024-07-01 » Профилирование кода в PHP
- 2024-06-28 » Изучаем ABC/XYZ-анализ: что это такое и какие решения с помощью него принимают
- 2024-06-17 » Зачем вам знать потребности клиента
- 2024-06-11 » Что нового в работе Яндекс Метрики: полный обзор обновления
- 2024-06-11 » Поведенческие факторы ранжирования в Яндексе
- 2024-06-11 » Скорость загрузки сайта: почему это важно и как влияет на ранжирование
Все мы сидим в сточной канаве, но некоторые при этом смотрят на звезды Уайльд Оскар - (1854-1900) - английский писатель |
Мы создаем сайты, которые работают! Профессионально обслуживаем и продвигаем их , а также по всей России и ближнему зарубежью с 2006 года!
Как мы работаем
Заявка
Позвоните или оставьте заявку на сайте.
Консультация
Обсуждаем что именно Вам нужно и помогаем определить как это лучше сделать!
Договор
Заключаем договор на оказание услуг, в котором прописаны условия и обязанности обеих сторон.
Выполнение работ
Непосредственно оказание требующихся услуг и работ по вашему заданию.
Поддержка
Сдача выполненых работ, последующие корректировки и поддержка при необходимости.