你清楚如何用PHP实现基于角色的访问控制吗

wen PHP项目 41

本文目录导读:

你清楚如何用PHP实现基于角色的访问控制吗

  1. 数据库设计
  2. 核心RBAC类实现
  3. 中间件实现
  4. 实际应用示例
  5. 高级功能:缓存优化
  6. 最佳实践建议

是的,我很清楚如何用PHP实现基于角色的访问控制(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,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 角色表
CREATE TABLE roles (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50) UNIQUE NOT NULL,
    description VARCHAR(255),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 权限表
CREATE TABLE permissions (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100) UNIQUE NOT NULL,
    slug VARCHAR(100) UNIQUE NOT NULL,
    description VARCHAR(255),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 用户角色关联表
CREATE TABLE user_roles (
    user_id INT NOT NULL,
    role_id INT NOT NULL,
    assigned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (user_id, role_id),
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
    FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE
);
-- 角色权限关联表
CREATE TABLE role_permissions (
    role_id INT NOT NULL,
    permission_id INT NOT NULL,
    PRIMARY KEY (role_id, permission_id),
    FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE,
    FOREIGN KEY (permission_id) REFERENCES permissions(id) ON DELETE CASCADE
);

核心RBAC类实现

<?php
class RBAC {
    private $db;
    private $cache = [];
    public function __construct($db) {
        $this->db = $db;
    }
    /**
     * 为用户分配角色
     */
    public function assignRole($userId, $roleId) {
        $stmt = $this->db->prepare("INSERT INTO user_roles (user_id, role_id) VALUES (?, ?)");
        return $stmt->execute([$userId, $roleId]);
    }
    /**
     * 移除用户角色
     */
    public function removeRole($userId, $roleId) {
        $stmt = $this->db->prepare("DELETE FROM user_roles WHERE user_id = ? AND role_id = ?");
        return $stmt->execute([$userId, $roleId]);
    }
    /**
     * 获取用户的所有角色
     */
    public function getUserRoles($userId) {
        $cacheKey = "user_roles_{$userId}";
        if (isset($this->cache[$cacheKey])) {
            return $this->cache[$cacheKey];
        }
        $stmt = $this->db->prepare("
            SELECT r.* FROM roles r
            JOIN user_roles ur ON r.id = ur.role_id
            WHERE ur.user_id = ?
        ");
        $stmt->execute([$userId]);
        $roles = $stmt->fetchAll(PDO::FETCH_ASSOC);
        $this->cache[$cacheKey] = $roles;
        return $roles;
    }
    /**
     * 获取用户的所有权限
     */
    public function getUserPermissions($userId) {
        $cacheKey = "user_permissions_{$userId}";
        if (isset($this->cache[$cacheKey])) {
            return $this->cache[$cacheKey];
        }
        $stmt = $this->db->prepare("
            SELECT DISTINCT p.* FROM permissions p
            JOIN role_permissions rp ON p.id = rp.permission_id
            JOIN user_roles ur ON rp.role_id = ur.role_id
            WHERE ur.user_id = ?
        ");
        $stmt->execute([$userId]);
        $permissions = $stmt->fetchAll(PDO::FETCH_ASSOC);
        $this->cache[$cacheKey] = $permissions;
        return $permissions;
    }
    /**
     * 检查用户是否有特定权限
     */
    public function hasPermission($userId, $permissionSlug) {
        $permissions = $this->getUserPermissions($userId);
        $slugs = array_column($permissions, 'slug');
        return in_array($permissionSlug, $slugs);
    }
    /**
     * 检查用户是否拥有某个角色
     */
    public function hasRole($userId, $roleName) {
        $roles = $this->getUserRoles($userId);
        $names = array_column($roles, 'name');
        return in_array($roleName, $names);
    }
    /**
     * 检查用户是否拥有所有指定权限(AND逻辑)
     */
    public function hasAllPermissions($userId, array $permissionSlugs) {
        foreach ($permissionSlugs as $slug) {
            if (!$this->hasPermission($userId, $slug)) {
                return false;
            }
        }
        return true;
    }
    /**
     * 检查用户是否拥有任一指定权限(OR逻辑)
     */
    public function hasAnyPermission($userId, array $permissionSlugs) {
        foreach ($permissionSlugs as $slug) {
            if ($this->hasPermission($userId, $slug)) {
                return true;
            }
        }
        return false;
    }
}

中间件实现

<?php
class RBACMiddleware {
    private $rbac;
    private $session;
    public function __construct($rbac, $session) {
        $this->rbac = $rbac;
        $this->session = $session;
    }
    /**
     * 权限检查中间件
     */
    public function requirePermission($permissionSlug, $redirectUrl = '/login') {
        if (!$this->session->isLoggedIn()) {
            header("Location: {$redirectUrl}");
            exit;
        }
        $userId = $this->session->getUserId();
        if (!$this->rbac->hasPermission($userId, $permissionSlug)) {
            http_response_code(403);
            echo "Access Denied: You don't have the required permission.";
            exit;
        }
    }
    /**
     * 角色检查中间件
     */
    public function requireRole($roleName, $redirectUrl = '/login') {
        if (!$this->session->isLoggedIn()) {
            header("Location: {$redirectUrl}");
            exit;
        }
        $userId = $this->session->getUserId();
        if (!$this->rbac->hasRole($userId, $roleName)) {
            http_response_code(403);
            echo "Access Denied: You don't have the required role.";
            exit;
        }
    }
}

实际应用示例

<?php
// 初始化
$db = new PDO("mysql:host=localhost;dbname=your_database", "username", "password");
$rbac = new RBAC($db);
// 1. 创建角色和权限
// 添加权限
$stmt = $db->prepare("INSERT INTO permissions (name, slug, description) VALUES (?, ?, ?)");
$stmt->execute(['创建文章', 'article.create', '允许创建新文章']);
$stmt->execute(['编辑文章', 'article.edit', '允许编辑现有文章']);
$stmt->execute(['删除文章', 'article.delete', '允许删除文章']);
$stmt->execute(['管理用户', 'user.manage', '允许管理用户账户']);
// 添加角色
$stmt = $db->prepare("INSERT INTO roles (name, description) VALUES (?, ?)");
$stmt->execute(['admin', '系统管理员']);
$stmt->execute(['editor', '内容编辑']);
$stmt->execute(['user', '普通用户']);
// 2. 分配权限给角色
// 管理员拥有所有权限
$stmt = $db->prepare("INSERT INTO role_permissions (role_id, permission_id) VALUES (?, ?)");
// 假设admin角色ID=1,所有权限ID 1-4
foreach ([1,2,3,4] as $permId) {
    $stmt->execute([1, $permId]);
}
// 编辑者拥有创建和编辑文章的权限
foreach ([1,2] as $permId) {
    $stmt->execute([2, $permId]);
}
// 3. 用户登录和使用RBAC
session_start();
class SessionManager {
    public function isLoggedIn() {
        return isset($_SESSION['user_id']);
    }
    public function getUserId() {
        return $_SESSION['user_id'] ?? null;
    }
}
$session = new SessionManager();
$middleware = new RBACMiddleware($rbac, $session);
// 模拟用户登录
$_SESSION['user_id'] = 1; // admin用户
// 4. 路由保护示例
// 文章创建页面
if ($_SERVER['REQUEST_URI'] === '/article/create') {
    $middleware->requirePermission('article.create', '/login');
    // 继续处理文章创建逻辑
    echo "创建文章页面";
}
// 文章管理页面
if ($_SERVER['REQUEST_URI'] === '/admin/users') {
    $middleware->requireRole('admin', '/login');
    // 继续处理用户管理逻辑
    echo "用户管理页面";
}
// 5. 细粒度权限检查
$userId = $session->getUserId();
if ($rbac->hasPermission($userId, 'article.edit')) {
    // 显示编辑按钮
}
// 检查是否同时拥有多个权限
if ($rbac->hasAllPermissions($userId, ['article.create', 'article.edit'])) {
    // 显示完整功能
}

高级功能:缓存优化

<?php
class CachedRBAC extends RBAC {
    private $cacheExpiry = 300; // 5分钟缓存
    public function getUserPermissions($userId) {
        $cacheKey = "user_permissions_{$userId}";
        $cached = apcu_fetch($cacheKey);
        if ($cached !== false) {
            return $cached;
        }
        $permissions = parent::getUserPermissions($userId);
        apcu_store($cacheKey, $permissions, $this->cacheExpiry);
        return $permissions;
    }
    // 当权限发生变化时清除缓存
    public function clearUserCache($userId) {
        apcu_delete("user_roles_{$userId}");
        apcu_delete("user_permissions_{$userId}");
    }
}

最佳实践建议

  1. 性能优化:使用缓存(Redis/Memcached)存储用户权限
  2. 懒加载:只在需要时加载权限数据
  3. 权限分层:设计合理的权限层级结构
  4. 审计日志:记录所有权限变更操作
  5. 测试覆盖:为RBAC逻辑编写单元测试
  6. 扩展性:考虑使用策略模式处理复杂权限规则

这个RBAC实现提供了完整的用户、角色、权限管理功能,可以灵活应对各种权限控制需求,你可以根据实际项目需求进行调整和扩展。

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