本文目录导读:

在 PHP 中实现访问控制列表(ACL,Access Control List)有多种方式,从简单的硬编码到基于数据库的动态权限系统,以下是几种常见的实现方法:
基于角色的简单 ACL(推荐新手使用)
<?php
class SimpleACL {
private $roles = [
'guest' => ['view'],
'user' => ['view', 'create', 'edit'],
'admin' => ['view', 'create', 'edit', 'delete', 'manage']
];
private $userRole;
public function __construct($role = 'guest') {
$this->userRole = $role;
}
public function hasPermission($permission) {
return in_array($permission, $this->roles[$this->userRole]);
}
public function checkAccess($permission) {
if (!$this->hasPermission($permission)) {
throw new Exception("权限不足");
}
return true;
}
}
// 使用示例
$user = new SimpleACL('user');
if ($user->hasPermission('create')) {
echo "可以创建文章";
}
?>
数据库驱动的 ACL 系统
数据库表结构
-- 用户表
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50),
role_id INT
);
-- 角色表
CREATE TABLE roles (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50)
);
-- 权限表
CREATE TABLE permissions (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100),
description TEXT
);
-- 角色-权限关联表
CREATE TABLE role_permissions (
role_id INT,
permission_id INT,
PRIMARY KEY (role_id, permission_id)
);
PHP 实现
<?php
class DBAcl {
private $db;
private $cache = [];
public function __construct($dbConnection) {
$this->db = $dbConnection;
}
public function getUserPermissions($userId) {
// 使用缓存避免重复查询
if (isset($this->cache[$userId])) {
return $this->cache[$userId];
}
$stmt = $this->db->prepare("
SELECT p.name
FROM permissions p
JOIN role_permissions rp ON p.id = rp.permission_id
JOIN users u ON u.role_id = rp.role_id
WHERE u.id = ?
");
$stmt->execute([$userId]);
$permissions = $stmt->fetchAll(PDO::FETCH_COLUMN);
$this->cache[$userId] = $permissions;
return $permissions;
}
public function checkPermission($userId, $permissionName) {
$permissions = $this->getUserPermissions($userId);
return in_array($permissionName, $permissions);
}
}
// 使用示例
$acl = new DBAcl($pdo);
if ($acl->checkPermission(1, 'article.create')) {
echo "用户有创建文章的权限";
}
?>
使用现有库:phpCAS(更专业)
安装
composer require casbin/casbin
使用示例
<?php
require 'vendor/autoload.php';
use Casbin\Enforcer;
// 创建权限模型
$enforcer = new Enforcer('path/to/model.conf', 'path/to/policy.csv');
// 检查权限
if ($enforcer->enforce('alice', 'data1', 'read')) {
echo "alice 可以读取 data1";
} else {
echo "权限不足";
}
// 添加权限
$enforcer->addPermissionForUser('alice', 'data1', 'write');
// 获取用户所有权限
$permissions = $enforcer->getPermissionsForUser('alice');
?>
完整的企业级 ACL 实现
<?php
class AccessControlList {
private $rules = [];
private $user;
public function __construct($user) {
$this->user = $user;
$this->loadRules();
}
private function loadRules() {
// 从配置文件或数据库加载规则
$this->rules = [
'articles' => [
'view' => ['*'], // 所有角色
'create' => ['admin', 'editor'],
'edit' => ['admin', 'editor'],
'delete' => ['admin']
],
'users' => [
'view' => ['admin'],
'manage' => ['admin']
]
];
}
public function isAllowed($resource, $action) {
if (!isset($this->rules[$resource][$action])) {
return false;
}
$allowedRoles = $this->rules[$resource][$action];
if (in_array('*', $allowedRoles)) {
return true;
}
return in_array($this->user->role, $allowedRoles);
}
public function requirePermission($resource, $action) {
if (!$this->isAllowed($resource, $action)) {
throw new PermissionDeniedException("权限不足");
}
return true;
}
}
// 中间件使用示例
class AuthMiddleware {
public function handle($request, $next) {
$acl = new AccessControlList($request->user);
// 检查当前路由的权限
$resource = $request->route['resource'];
$action = $request->route['action'];
try {
$acl->requirePermission($resource, $action);
return $next($request);
} catch (PermissionDeniedException $e) {
return response()->json(['error' => '权限不足'], 403);
}
}
}
?>
性能优化技巧
<?php
class OptimizedACL {
private static $instance;
private $permissionsCache = [];
private $cacheTimeout = 3600; // 1小时
public static function getInstance() {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
// 使用 Redis 缓存
public function checkPermissionWithRedis($userId, $permission) {
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$cacheKey = "acl:user:{$userId}:{$permission}";
// 检查缓存
if ($redis->exists($cacheKey)) {
return (bool)$redis->get($cacheKey);
}
// 数据库查询
$result = $this->queryDatabase($userId, $permission);
// 设置缓存
$redis->setex($cacheKey, $this->cacheTimeout, $result ? 1 : 0);
return $result;
}
}
?>
选择建议
- 小型项目:使用基于角色的简单 ACL(方法1)
- 中型项目:使用数据库驱动的 ACL(方法2)
- 大型/企业级项目:使用专业库如 phpCAS(方法3)
- 需要高性能:添加 Redis 缓存(方法5)
推荐从简单方法开始,根据项目需求逐步升级,权限系统宁严勿松,后期放宽比收紧容易得多。