RBAC(Role-Based Access Control,基于角色的访问控制)是目前 Web 系统中最通用、最成熟的权限架构。它的核心思想是:用户不直接与权限关联,而是通过“角色”作为中间层。
1. 核心设计思路
在 RBAC 模型中,我们需要处理三类实体和两组多对多关系:
- 用户 (User):系统的使用者。
- 角色 (Role):用户在系统中的身份(如管理员、财务、编辑)。
- 权限 (Permission):具体的操作许可(如“发布文章”、“审核财务”)。
关系流转: 用户 -> 拥有角色 -> 角色关联权限 -> 用户获得权限
2. 数据库设计
这是 RBAC 的最小完备集(Minimum Viable Product),由 5 张表构成。
2.1 实体表
1. users(用户表) 存储基础用户信息。
CREATE TABLE users (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL COMMENT '登录名',
password CHAR(60) NOT NULL COMMENT '加密后的密码',
status TINYINT NOT NULL DEFAULT 1 COMMENT '1=正常, 0=禁用'
) COMMENT='用户基础表';
安全提示:密码严禁明文存储。推荐使用 bcrypt 算法(PHP
password_hash或crypt)。
2. roles(角色表) 定义系统中的身份。
CREATE TABLE roles (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL COMMENT '唯一标识, e.g. admin',
display_name VARCHAR(100) NOT NULL COMMENT '显示名称, e.g. 管理员',
description VARCHAR(255) DEFAULT NULL
) COMMENT='角色定义表';
3. permissions(权限点表) 定义系统中所有可控的“资源+动作”。
CREATE TABLE permissions (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL COMMENT '唯一标识, e.g. post.create',
display_name VARCHAR(150) NOT NULL COMMENT '显示名称',
description VARCHAR(255) DEFAULT NULL
) COMMENT='具体权限点定义表';
最佳实践:权限 Key 推荐使用
资源.动作格式,例如:
user.view,user.create,user.deletefinance.audit,finance.export
2.2 关联表(中间表)
4. user_role(用户-角色关联) 实现用户与角色的多对多关系(一个用户可以是多个角色)。
CREATE TABLE user_role (
user_id INT UNSIGNED NOT NULL,
role_id INT UNSIGNED NOT NULL,
PRIMARY KEY (user_id, role_id)
) COMMENT='用户角色关联表';
5. role_permission(角色-权限关联) 实现角色与权限的多对多关系(一个角色拥有多个权限)。
CREATE TABLE role_permission (
role_id INT UNSIGNED NOT NULL,
permission_id INT UNSIGNED NOT NULL,
PRIMARY KEY (role_id, permission_id)
) COMMENT='角色权限关联表';
3. 后端核心代码实现 (PHP)
为了保证系统的可维护性,我们将权限逻辑封装在独立的层中。
目录结构建议:
/include/
├── auth_core.php # 核心逻辑:登录验证、权限加载、检查函数
├── bootstrap_auth.php # 引导文件:统一初始化 Session 和 Auth
/finance/
├── report.php # 业务页面
3.1 核心逻辑 (auth_core.php)
<?php
// include/auth_core.php
require_once __DIR__ . '/../config/database.php';
/**
* 启动安全 Session
*/
function auth_start_session()
{
if (session_id() == '') {
session_name(SESSION_NAME);
session_start();
}
}
/**
* 用户登录处理
*/
function auth_login($username, $password)
{
$pdo = db_connect();
// 务必检查 status = 1 (激活状态)
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ? AND status = 1 LIMIT 1");
$stmt->execute(array($username));
$user = $stmt->fetch();
if (!$user) {
return false;
}
// 密码校验 (兼容 PHP 5.3+ crypt 方式,PHP 5.5+ 建议用 password_verify)
if (crypt($password, $user['password']) !== $user['password']) {
return false;
}
// --- 登录成功核心逻辑 ---
// 1. 一次性加载该用户所有权限和角色
$permissions = auth_load_permissions_for_user($user['id']);
$roles = auth_load_roles_for_user($user['id']);
// 2. 将权限写入 Session (避免每次刷新页面都查库)
$_SESSION['user'] = array(
'id' => $user['id'],
'username' => $user['username'],
'roles' => $roles, // 数组: ['admin', 'editor']
'permissions' => $permissions, // 数组: ['post.create', 'post.view']
);
// 3. 安全防护:重置 Session ID,防止会话固定攻击
session_regenerate_id(true);
return true;
}
/**
* 注销
*/
function auth_logout()
{
auth_start_session();
$_SESSION = array();
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000,
$params["path"], $params["domain"],
$params["secure"], $params["httponly"]
);
}
session_destroy();
}
/**
* 获取当前登录用户信息
*/
function auth_user()
{
auth_start_session();
return isset($_SESSION['user']) ? $_SESSION['user'] : null;
}
/**
* 检查是否已登录
*/
function auth_check()
{
return auth_user() !== null;
}
// ========== 数据库查询辅助函数 ==========
function auth_load_roles_for_user($userId)
{
$pdo = db_connect();
$sql = "SELECT r.name FROM roles r
JOIN user_role ur ON ur.role_id = r.id
WHERE ur.user_id = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute(array($userId));
return $stmt->fetchAll(PDO::FETCH_COLUMN); // 直接返回一维数组
}
function auth_load_permissions_for_user($userId)
{
$pdo = db_connect();
// 使用 DISTINCT 去重(因为用户可能有多个角色,角色间可能有重叠权限)
$sql = "SELECT DISTINCT p.name
FROM permissions p
JOIN role_permission rp ON rp.permission_id = p.id
JOIN user_role ur ON ur.role_id = rp.role_id
WHERE ur.user_id = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute(array($userId));
return $stmt->fetchAll(PDO::FETCH_COLUMN);
}
// ========== 权限检查核心 API ==========
/**
* 检查当前用户是否有特定权限
* @param string $permission 权限Key (e.g. 'post.create')
* @return bool
*/
function auth_can($permission)
{
$user = auth_user();
if (!$user) {
return false;
}
// 【超级管理员后门】:如果是 super_admin,拥有所有权限
if (in_array('super_admin', $user['roles'], true)) {
return true;
}
// 严格匹配权限
return in_array($permission, $user['permissions'], true);
}
/**
* 强制检查权限,无权则终止执行
*/
function auth_require_permission($permission)
{
// 1. 先检查登录
if (!auth_check()) {
header('Location: /login.php');
exit;
}
// 2. 再检查权限
if (!auth_can($permission)) {
header('HTTP/1.1 403 Forbidden');
echo 'Access Denied';
exit;
}
}
3.2 统一引导文件 (bootstrap_auth.php)
<?php
// include/bootstrap_auth.php
// 所有业务页面头部必须引用此文件
require_once __DIR__ . '/auth_core.php';
// 初始化 Session
auth_start_session();
// 可以在这里做全局的 Session 过期检查或单一登录检查
3.3 业务页面接入示例
<?php
// finance/report.php
require_once __DIR__ . '/../include/bootstrap_auth.php';
// 1. 这一行代码就完成了“未登录跳转”和“无权拦截”
auth_require_permission('finance.view_report');
// --- 下面是业务代码,只有拥有权限的人才能看到 ---
?>
<h1>财务报表</h1>
<?php if (auth_can('finance.export')): ?>
<button>导出 Excel</button>
<?php endif; ?>
4. 最佳实践总结
-
零信任原则 (Default Deny):
auth_can()默认返回false。- 没有明确赋予权限的操作,一律视为禁止。
-
超级管理员 (Super Admin):
- 在代码层面为
super_admin角色留“后门”(return true),防止因为数据库配置错误导致所有人都无法登录后台修复数据。
- 在代码层面为
-
权限缓存:
- 本文示例将权限放入了
$_SESSION。优点是性能高(不用每次刷新查库),缺点是修改用户权限后,用户需重新登录才能生效。 - 进阶方案:在 Session 中记录
permission_version,每次请求对比数据库版本号,若过期则重载。
- 本文示例将权限放入了
-
前端隐藏 vs 后端拦截:
- 前端
if (auth_can(...))隐藏按钮只是为了用户体验,绝不能作为安全措施。 - 所有数据接口(API)和页面入口必须有后端的
auth_require_permission()拦截。
- 前端
5. 权限模型对比
当 RBAC 无法满足需求(如需要控制“只能上午访问”、“只能访问自己创建的数据”)时,可以考虑以下模型:
| 模型 | 全称 | 核心逻辑 | 适用场景 | 复杂度 |
|---|---|---|---|---|
| ACL | Access Control List | 用户 <-> 资源 |
个人网盘、极简单的后台 | ⭐ |
| RBAC | Role-Based Access Control | 用户 <-> 角色 <-> 权限 |
绝大多数企业后台、SaaS 系统 | ⭐⭐ |
| ABAC | Attribute-Based Access Control | 属性(如时间、地点、部门)动态计算 | 公有云 IAM、高安全级银行系统 | ⭐⭐⭐⭐ |
| MAC | Mandatory Access Control | 强制标签(绝密/机密) | 军工、涉密系统 | ⭐⭐⭐ |
| ReBAC | Relationship-Based Access Control | 基于关系图谱(我是 Owner 就能改) | 社交网络、Google Drive 文档协作 | ⭐⭐⭐⭐⭐ |