PHP多角色权限控制方案

wen PHP项目 2

本文目录导读:

PHP多角色权限控制方案

  1. 最简方案:基于角色的硬编码
  2. 推荐方案:RBAC(基于角色的访问控制)
  3. 进阶方案:ACL(访问控制列表)
  4. 性能优化建议
  5. 总结建议

在PHP中实现多角色权限控制,有几种成熟的方案可供选择,我为你整理了一套从基础到进阶的完整实践方案:

最简方案:基于角色的硬编码

适合小型项目或原型开发,通过简单的角色判断来控制访问权限。

<?php
// 定义角色常量
const ROLE_ADMIN = 'admin';      // 超级管理员
const ROLE_MANAGER = 'manager';  // 运营经理
const ROLE_EDITOR = 'editor';    // 内容编辑
const ROLE_USER = 'user';        // 普通用户
// 简单的权限判断
if ($_SESSION['role'] === ROLE_ADMIN) {
    // 管理员专属功能
    echo "你可以管理所有用户";
} elseif ($_SESSION['role'] === ROLE_MANAGER) {
    // 经理可以审核文章
    echo "你可以审核文章";
} else {
    // 普通用户只能看
    echo "你只能浏览内容";
}

优点:简单直接,代码量少 缺点:不易扩展,角色多了会比较混乱


推荐方案:RBAC(基于角色的访问控制)

这是目前最主流、最推荐的方案,适合中小型项目,包括三个核心表:

数据库设计

-- 用户表
CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) UNIQUE NOT NULL,
    password VARCHAR(255) NOT NULL,
    email VARCHAR(100),
    status TINYINT DEFAULT 1,  -- 1启用 0禁用
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 角色表
CREATE TABLE roles (
    id INT PRIMARY KEY AUTO_INCREMENT,
    role_name VARCHAR(50) UNIQUE NOT NULL,
    description VARCHAR(255),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 权限表
CREATE TABLE permissions (
    id INT PRIMARY KEY AUTO_INCREMENT,
    perm_key VARCHAR(50) UNIQUE NOT NULL,  -- 权限标识,如 user:create
    perm_name VARCHAR(100) NOT NULL,       -- 权限名称,如 创建用户
    module VARCHAR(50)                     -- 所属模块
);
-- 用户-角色关联表(多对多)
CREATE TABLE user_roles (
    id INT PRIMARY KEY AUTO_INCREMENT,
    user_id INT NOT NULL,
    role_id INT NOT NULL,
    FOREIGN KEY (user_id) REFERENCES users(id),
    FOREIGN KEY (role_id) REFERENCES roles(id),
    UNIQUE KEY (user_id, role_id)  -- 防止重复分配
);
-- 角色-权限关联表(多对多)
CREATE TABLE role_permissions (
    id INT PRIMARY KEY AUTO_INCREMENT,
    role_id INT NOT NULL,
    permission_id INT NOT NULL,
    FOREIGN KEY (role_id) REFERENCES roles(id),
    FOREIGN KEY (permission_id) REFERENCES permissions(id),
    UNIQUE KEY (role_id, permission_id)
);

代码实现

权限检查类

<?php
class Auth {
    private $db;
    private $user;
    public function __construct($db) {
        $this->db = $db;
        $this->user = $this->getCurrentUser();
    }
    // 获取当前登录用户
    private function getCurrentUser() {
        if (isset($_SESSION['user_id'])) {
            $userId = (int)$_SESSION['user_id'];
            $query = "SELECT * FROM users WHERE id = ? AND status = 1";
            $stmt = $this->db->prepare($query);
            $stmt->execute([$userId]);
            return $stmt->fetch(PDO::FETCH_ASSOC);
        }
        return null;
    }
    // 获取用户所有权限
    public function getUserPermissions($userId = null) {
        $userId = $userId ?: $this->user['id'];
        $query = "SELECT DISTINCT p.perm_key 
                  FROM permissions p 
                  INNER JOIN role_permissions rp ON p.id = rp.permission_id
                  INNER JOIN user_roles ur ON rp.role_id = ur.role_id
                  WHERE ur.user_id = ?";
        $stmt = $this->db->prepare($query);
        $stmt->execute([$userId]);
        return $stmt->fetchAll(PDO::FETCH_COLUMN);
    }
    // 检查用户是否有某权限
    public function hasPermission($permKey) {
        if (!$this->user) return false;
        // 每次检查都查数据库,性能不佳,可以使用Session缓存
        $permissions = $this->getUserPermissions();
        return in_array($permKey, $permissions);
    }
    // 检查用户是否有某个角色
    public function hasRole($roleName) {
        if (!$this->user) return false;
        $query = "SELECT r.role_name 
                  FROM roles r 
                  INNER JOIN user_roles ur ON r.id = ur.role_id
                  WHERE ur.user_id = ?";
        $stmt = $this->db->prepare($query);
        $stmt->execute([$this->user['id']]);
        $roles = $stmt->fetchAll(PDO::FETCH_COLUMN);
        return in_array($roleName, $roles);
    }
    // 在页面中使用
    public function requirePermission($permKey) {
        if (!$this->hasPermission($permKey)) {
            header('HTTP/1.1 403 Forbidden');
            echo json_encode(['error' => '没有权限执行此操作']);
            exit;
        }
    }
}

中间件使用示例(Laravel风格)

<?php
// 在控制器方法中使用
class UserController {
    private $auth;
    public function __construct($auth) {
        $this->auth = $auth;
    }
    public function create() {
        // 检查权限:user:create
        $this->auth->requirePermission('user:create');
        // 业务逻辑
        echo "创建用户成功";
    }
    public function delete($userId) {
        // 删除用户通常需要管理员权限
        $this->auth->requirePermission('user:delete');
        // 业务逻辑
        echo "删除用户成功";
    }
    public function export() {
        // 或者使用角色判断
        if (!$this->auth->hasRole('admin')) {
            die('只有管理员可以导出数据');
        }
        // 业务逻辑
        echo "导出数据成功";
    }
}

权限分配管理界面(管理员使用)

<?php
// assign_permissions.php
class RoleManager {
    private $db;
    public function assignRoleToUser($userId, $roleId) {
        // 先清理用户已有角色(可选)
        $query = "DELETE FROM user_roles WHERE user_id = ?";
        $this->db->prepare($query)->execute([$userId]);
        // 分配新角色
        $query = "INSERT INTO user_roles (user_id, role_id) VALUES (?, ?)";
        return $this->db->prepare($query)->execute([$userId, $roleId]);
    }
    public function syncRolePermissions($roleId, array $permissionIds) {
        // 同步角色权限
        $this->db->beginTransaction();
        try {
            // 删除旧的权限关联
            $query = "DELETE FROM role_permissions WHERE role_id = ?";
            $this->db->prepare($query)->execute([$roleId]);
            // 添加新的权限
            foreach ($permissionIds as $pid) {
                $query = "INSERT INTO role_permissions (role_id, permission_id) VALUES (?, ?)";
                $this->db->prepare($query)->execute([$roleId, $pid]);
            }
            $this->db->commit();
            return true;
        } catch (Exception $e) {
            $this->db->rollBack();
            return false;
        }
    }
}

进阶方案:ACL(访问控制列表)

在RBAC基础上更细粒度控制,支持按某个具体数据实例控制权限。

修改数据表结构

-- 增加资源表
CREATE TABLE resources (
    id INT PRIMARY KEY AUTO_INCREMENT,
    resource_type VARCHAR(50) NOT NULL,  -- 如:post, article, comment
    resource_id INT NOT NULL,            -- 资源ID
    owner_id INT NOT NULL,               -- 资源拥有者
    created_at TIMESTAMP
);
-- 用户-资源-操作 关联表
CREATE TABLE user_resource_permissions (
    id INT PRIMARY KEY AUTO_INCREMENT,
    user_id INT NOT NULL,
    resource_id INT NOT NULL,
    permission_type VARCHAR(50),  -- view, edit, delete
    granted BOOLEAN DEFAULT 1     -- 是否允许
);

实现单个资源权限检查

<?php
class ACL extends Auth {
    // 检查用户是否有权限操作某个资源
    public function canOperate($userId, $resourceType, $resourceId, $operation) {
        // 超级管理员直接放行
        if ($this->hasRole('admin')) return true;
        // 检查资源归属
        $resourceQuery = "SELECT * FROM resources 
                          WHERE resource_type = ? AND resource_id = ?";
        $resource = $this->db->prepare($resourceQuery);
        $resource->execute([$resourceType, $resourceId]);
        $resourceData = $resource->fetch(PDO::FETCH_ASSOC);
        if (!$resourceData) return false;
        // 如果是自己的资源,允许操作(可配置规则)
        if ($resourceData['owner_id'] === $userId) {
            return true;
        }
        // 检查是否有特殊授权
        $authQuery = "SELECT * FROM user_resource_permissions 
                      WHERE user_id = ? AND resource_id = ? AND permission_type = ?";
        $authStmt = $this->db->prepare($authQuery);
        $authStmt->execute([$userId, $resourceData['id'], $operation]);
        return $authStmt->fetch() !== false;
    }
    // 在控制器中使用
    public function editArticle($articleId) {
        if (!$this->canOperate($this->user['id'], 'article', $articleId, 'edit')) {
            die('没有权限编辑这篇文章');
        }
        echo "文章编辑成功";
    }
}

性能优化建议

当系统用户量比较大时,需要优化权限检查性能:

<?php
class OptimizedAuth extends Auth {
    // 使用Redis缓存权限
    private $redis;
    public function __construct($db, $redis = null) {
        parent::__construct($db);
        $this->redis = $redis;
    }
    // 缓存用户的权限
    private function cachePermissions($userId) {
        $cacheKey = "user_permissions:{$userId}";
        // 检查缓存
        if ($this->redis->exists($cacheKey)) {
            return json_decode($this->redis->get($cacheKey), true);
        }
        // 缓存未命中,查询数据库
        $permissions = parent::getUserPermissions($userId);
        // 存入缓存,有效期1小时
        $this->redis->setex($cacheKey, 3600, json_encode($permissions));
        return $permissions;
    }
    // 重置缓存(在权限变更时调用)
    public function clearPermissionCache($userId) {
        $cacheKey = "user_permissions:{$userId}";
        $this->redis->del($cacheKey);
    }
    public function hasPermission($permKey, $userId = null) {
        $userId = $userId ?: $this->user['id'];
        $permissions = $this->cachePermissions($userId);
        return in_array($permKey, $permissions);
    }
}

总结建议

根据项目大小选择方案:

  1. 小型项目(个人博客,简单系统)→ 直接使用硬编码角色判断即可
  2. 中型项目(企业应用,内容管理系统)→ 推荐使用RBAC,完整实现多角色权限控制
  3. 大型复杂系统(多部门协作,数据隔离需求)→ 在RBAC基础上加入ACL支持

核心思路:

  • 理清业务需求,确定有多少种角色,多少种权限操作
  • 设计合理的数据结构
  • 将权限检查逻辑封装成可复用的类
  • 合理使用缓存技术提升性能

这样可以灵活应对多种场景,希望这些方案能帮到你。

抱歉,评论功能暂时关闭!