php 实现 rbac
实现 RBAC(基于角色的访问控制)的核心思路
RBAC(Role-Based Access Control)是一种常见的权限管理模型,通过角色关联用户和权限。PHP 实现 RBAC 通常包含以下关键组件:
- 用户表:存储用户信息
- 角色表:定义系统角色
- 权限表:定义具体权限
- 用户角色关联表:建立用户与角色关系
- 角色权限关联表:建立角色与权限关系
数据库结构设计
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
password VARCHAR(255) NOT NULL
);
CREATE TABLE roles (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
description VARCHAR(255)
);
CREATE TABLE permissions (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
description VARCHAR(255)
);
CREATE TABLE user_role (
user_id INT,
role_id INT,
PRIMARY KEY (user_id, role_id),
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (role_id) REFERENCES roles(id)
);
CREATE TABLE role_permission (
role_id INT,
permission_id INT,
PRIMARY KEY (role_id, permission_id),
FOREIGN KEY (role_id) REFERENCES roles(id),
FOREIGN KEY (permission_id) REFERENCES permissions(id)
);
核心权限检查类实现
class RBAC {
private $db;
public function __construct($dbConnection) {
$this->db = $dbConnection;
}
public function checkPermission($userId, $permissionName) {
$query = "SELECT COUNT(*) as count FROM permissions p
JOIN role_permission rp ON p.id = rp.permission_id
JOIN user_role ur ON rp.role_id = ur.role_id
WHERE ur.user_id = :user_id AND p.name = :permission_name";
$stmt = $this->db->prepare($query);
$stmt->bindParam(':user_id', $userId);
$stmt->bindParam(':permission_name', $permissionName);
$stmt->execute();
$result = $stmt->fetch(PDO::FETCH_ASSOC);
return $result['count'] > 0;
}
public function getUserRoles($userId) {
$query = "SELECT r.name FROM roles r
JOIN user_role ur ON r.id = ur.role_id
WHERE ur.user_id = :user_id";
$stmt = $this->db->prepare($query);
$stmt->bindParam(':user_id', $userId);
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_COLUMN, 0);
}
}
中间件实现权限验证
function checkPermission($permission) {
return function($request, $response, $next) use ($permission) {
$userId = $_SESSION['user_id'] ?? null;
if (!$userId) {
return $response->withStatus(403)->withJson(['error' => '未登录']);
}
$rbac = new RBAC($this->db);
if (!$rbac->checkPermission($userId, $permission)) {
return $response->withStatus(403)->withJson(['error' => '权限不足']);
}
return $next($request, $response);
};
}
缓存优化策略
为提高性能,可将用户权限缓存起来:
class RBACWithCache extends RBAC {
private $cache = [];
public function checkPermission($userId, $permissionName) {
$cacheKey = "user_{$userId}_permissions";
if (!isset($this->cache[$cacheKey])) {
$query = "SELECT p.name FROM permissions p
JOIN role_permission rp ON p.id = rp.permission_id
JOIN user_role ur ON rp.role_id = ur.role_id
WHERE ur.user_id = :user_id";
$stmt = $this->db->prepare($query);
$stmt->bindParam(':user_id', $userId);
$stmt->execute();
$this->cache[$cacheKey] = $stmt->fetchAll(PDO::FETCH_COLUMN, 0);
}
return in_array($permissionName, $this->cache[$cacheKey]);
}
}
实际应用示例
// 初始化RBAC
$db = new PDO('mysql:host=localhost;dbname=rbac', 'username', 'password');
$rbac = new RBAC($db);
// 检查权限
if ($rbac->checkPermission($currentUserId, 'edit_post')) {
// 允许编辑文章
} else {
// 显示权限不足
}
// 获取用户所有角色
$roles = $rbac->getUserRoles($currentUserId);
最佳实践建议
- 权限名称采用模块_操作格式,如article_edit、user_delete
- 超级管理员角色可跳过权限检查
- 定期清理无效的权限和角色
- 前端界面根据权限动态显示功能按钮
- 记录重要的权限操作日志
这种实现方式灵活且易于扩展,可根据具体项目需求调整数据库结构和权限检查逻辑。







