Ролевая модель и группы пользователей в PHP

Раздел: Разработка на PHP -> Управление пользователями в PHP

Группы пользователей в PHP: варианты реализации

Основное решение: реляционная база данных и ACL

Надёжный подход для большинства проектов - хранение групп и прав в реляционной базе данных (MySQL, PostgreSQL). Это обеспечивает целостность, масштабирование и возможность сложных запросов.

Пример структуры таблиц:


CREATE TABLE `groups` (
  `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  `name` VARCHAR(100) NOT NULL UNIQUE,
  `description` TEXT
);

CREATE TABLE `user_groups` (
  `user_id` INT UNSIGNED NOT NULL,
  `group_id` INT UNSIGNED NOT NULL,
  PRIMARY KEY (`user_id`, `group_id`),
  FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE,
  FOREIGN KEY (`group_id`) REFERENCES `groups`(`id`) ON DELETE CASCADE
);

CREATE TABLE `permissions` (
  `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  `group_id` INT UNSIGNED NOT NULL,
  `resource` VARCHAR(255) NOT NULL,
  `action` VARCHAR(50) NOT NULL,
  `allowed` TINYINT(1) DEFAULT 1,
  FOREIGN KEY (`group_id`) REFERENCES `groups`(`id`) ON DELETE CASCADE
);

User type php name (тип пользователя в php)

Класс для проверки прав пользователя:


class Acl {
    private \PDO $pdo;
    
    public function __construct(\PDO $pdo) {
        $this->pdo = $pdo;
    }
    
    public function isAllowed(int $userId, string $resource, string $action): bool {
        $sql = "SELECT COUNT(*) FROM user_groups ug
                JOIN permissions p ON ug.group_id = p.group_id
                WHERE ug.user_id = :user_id
                  AND p.resource = :resource
                  AND p.action = :action
                  AND p.allowed = 1";
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute([
            ':user_id'  => $userId,
            ':resource' => $resource,
            ':action'   => $action,
        ]);
        return (int) $stmt->fetchColumn() > 0;
    }
}

User group php (группа пользователей в php)

Использование:


$acl = new Acl($pdo);
if ($acl->isAllowed($currentUser->id, 'article', 'edit')) {
    // разрешено редактирование статьи
} else {
    // отказ доступа
}

Php user ip (ip-адрес пользователя в php)

Типичные ошибки и их решение

  • Проблема: Запросы к БД при каждом вызове isAllowed снижают производительность. Решение: кэшировать права пользователя в сессии или Redis.
  • Проблема: Избыточное количество записей в таблице permissions. Решение: ввести наследование групп (роли с родительскими ролями).

Как организовать группы пользователей без базы данных?

Для очень маленьких проектов или прототипов можно использовать ассоциативный массив с определением групп.


$groups = [
    1 => ['name' => 'admin', 'permissions' => ['article.*', 'user.*']],
    2 => ['name' => 'editor', 'permissions' => ['article.edit', 'article.view']],
];

$userGroups = [1 => [1], 2 => [2]]; // userId => [groupIds]

function userHasPermission(int $userId, string $resource, string $action): bool {
    global $groups, $userGroups;
    foreach ($userGroups[$userId] as $groupId) {
        $perms = $groups[$groupId]['permissions'];
        if (in_array("$resource.*", $perms) || in_array("$resource.$action", $perms)) {
            return true;
        }
    }
    return false;
}

Remote user php (удаленный пользователь в php)

Недостатки: сложность поддержки при росте проекта, отсутствие персистентности, данные теряются при перезапуске.

Как хранить группы пользователей в файле конфигурации?

Можно использовать ini- или json-файл для статических ролей. Пример на PHP с чтением JSON:


$config = json_decode(file_get_contents('groups.json'), true);
// groups.json: { "groups": [ { "id":1,"name":"admin","permissions":["*"] } ] }

User photo php (фото пользователя в php)

Этот вариант подходит для приложений, где состав групп меняется редко. Проблема: сложность синхронизации в распределённой среде.

При многопоточном доступе к файлу возможны конфликты записи. Решение - блокировки flock или переход на базу данных.

Как реализовать группы через сторонние библиотеки (например, Zend\Permissions\Acl)?

Библиотека предоставляет готовый ACL-движок. Установка через Composer:


composer require zendframework/zend-permissions-acl

Edits php id user (редактирование пользователя по id в php)

Пример использования:


use Zend\Permissions\Acl\Acl;
use Zend\Permissions\Acl\Role\GenericRole;
use Zend\Permissions\Acl\Resource\GenericResource;

$acl = new Acl();
$acl->addRole(new GenericRole('guest'));
$acl->addRole(new GenericRole('member'), 'guest'); // member наследует права guest
$acl->addRole(new GenericRole('admin'));

$acl->addResource(new GenericResource('article'));
$acl->addResource(new GenericResource('user'));

$acl->allow('guest', 'article', 'view');
$acl->allow('member', 'article', 'edit');
$acl->allow('admin', null, null); // admin имеет все права

// проверка
if ($acl->isAllowed('member', 'article', 'edit')) {
    echo 'Разрешено';
}

Типичная ошибка - забыть зарегистрировать ресурс или роль. Исключение Zend\Permissions\Acl\Exception\InvalidArgumentException сообщит о проблеме.

- Php get id user (получение id пользователя в php)
- User php mode (режим пользователя в php)
- Php create user (создание пользователя в php)

Расширенные примеры работы с группами пользователей в PHP

Пример 1: ACL с наследованием ролей и кэшированием в Redis

Код класса Acl с кэшированием:

Пример

class Acl {
    private \PDO $pdo;
    private \Redis $redis;
    private int $ttl = 3600;
    
    public function __construct(\PDO $pdo, \Redis $redis) {
        $this->pdo = $pdo;
        $this->redis = $redis;
    }
    
    public function isAllowed(int $userId, string $resource, string $action): bool {
        $cacheKey = "acl:$userId:$resource:$action";
        $cached = $this->redis->get($cacheKey);
        if ($cached !== false) {
            return $cached === '1';
        }
        $result = $this->checkDatabase($userId, $resource, $action);
        $this->redis->setex($cacheKey, $this->ttl, $result ? '1' : '0');
        return $result;
    }
    
    private function checkDatabase(int $userId, string $resource, string $action): bool {
        // запрос с учётом наследования ролей (parent group)
        $sql = "SELECT COUNT(*) FROM user_groups ug
                JOIN groups g ON ug.group_id = g.id
                LEFT JOIN permissions p ON (
                    p.group_id = g.id OR
                    p.group_id IN (SELECT parent_id FROM groups WHERE id = g.id)
                )
                WHERE ug.user_id = :user_id
                  AND p.resource = :resource
                  AND p.action = :action
                  AND p.allowed = 1";
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute([
            ':user_id'  => $userId,
            ':resource' => $resource,
            ':action'   => $action,
        ]);
        return (int) $stmt->fetchColumn() > 0;
    }
}

Результат при первом вызове:

После выполнения запроса к БД результат кэшируется в Redis на 1 час.
При повторных вызовах данные берутся из кэша, что ускоряет проверку доступа в 10-50 раз.

Пример 2: Интеграция с фреймворком Laravel (использование Gates и policies)

В Laravel группы пользователей можно реализовать через кастомные Guard или использовать пакет Spatie Permissions. Пример с Spatie:

Пример

// Установка:
composer require spatie/laravel-permission

// Создание ролей и прав:
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;

$admin = Role::create(['name' => 'admin']);
$editArticle = Permission::create(['name' => 'edit articles']);
$admin->givePermissionTo($editArticle);

$user = User::find(1);
$user->assignRole('admin');

// Проверка в Blade:
@can('edit articles')
    // ссылка на редактирование
@endcan

// В контроллере:
if ($user->can('edit articles')) {
    // разрешено
}

Результат:

Пользователь с ролью admin получает право 'edit articles'.
При проверке в шаблоне или контроллере Laravel автоматически обращается к базе данных.

Пример 3: Сложный ACL с несколькими ресурсами и условиями (на чистом PHP)

Пример

class AdvancedAcl {
    private array $rules = [];
    
    public function addRule(string $role, string $resource, string $action, bool $allow, callable $condition = null): void {
        $this->rules[] = compact('role', 'resource', 'action', 'allow', 'condition');
    }
    
    public function check(string $role, string $resource, string $action, ...$context): bool {
        $matched = false;
        foreach ($this->rules as $rule) {
            if ($rule['role'] === $role && $rule['resource'] === $resource && $rule['action'] === $action) {
                $conditionPassed = $rule['condition'] ? $rule['condition'](...$context) : true;
                if ($conditionPassed) {
                    $matched = $rule['allow'];
                }
            }
        }
        return $matched;
    }
}

$acl = new AdvancedAcl();
$acl->addRule('editor', 'article', 'edit', true, function($userId, $articleOwnerId) {
    return $userId === $articleOwnerId; // только владелец может редактировать
});

$result = $acl->check('editor', 'article', 'edit', 5, 5);
echo $result ? 'Разрешено' : 'Запрещено';

Результат выполнения:

Разрешено (так как userId=5 совпадает с $articleOwnerId=5)

Данный пример демонстрирует гибкость с динамическими условиями для проверки прав.

Группа пользователей в PHP - comments

En
User group php (php)