免费资源下载
作者:技术架构师 | 发布日期:2023年10月
一、项目架构设计与技术选型
在大型企业应用开发中,权限管理是不可或缺的核心模块。本文将基于ThinkPHP 6.0的多应用模式,构建一个高可扩展的权限管理系统(RBAC)。与传统教程不同,我们将采用领域驱动设计(DDD)思想进行模块划分。
1.1 项目结构设计
app/
├── admin/ # 后台管理应用
│ ├── controller/
│ ├── service/ # 业务逻辑层
│ └── validate/
├── common/ # 公共模块
│ ├── library/ # 核心库
│ ├── model/ # 领域模型
│ └── trait/ # 特性封装
└── api/ # API接口应用
二、核心数据库设计
采用五表RBAC模型,支持多级权限和角色继承:
2.1 数据表结构
-- 用户表
CREATE TABLE `sys_user` (
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL COMMENT '用户名',
`password_hash` varchar(255) NOT NULL COMMENT '加密密码',
`realname` varchar(50) DEFAULT NULL COMMENT '真实姓名',
`status` tinyint(1) DEFAULT '1' COMMENT '状态:1正常 0禁用',
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 角色表(支持角色层级)
CREATE TABLE `sys_role` (
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` varchar(30) NOT NULL COMMENT '角色名称',
`parent_id` int(11) DEFAULT '0' COMMENT '父角色ID',
`level` tinyint(3) DEFAULT '1' COMMENT '角色层级',
`description` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 权限节点表(支持RESTful风格)
CREATE TABLE `sys_permission` (
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL COMMENT '权限名称',
`code` varchar(100) NOT NULL COMMENT '权限标识',
`type` enum('menu','button','api') DEFAULT 'menu',
`path` varchar(255) DEFAULT NULL COMMENT '路由路径',
`parent_id` int(11) DEFAULT '0',
`sort` int(11) DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `code` (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 角色-权限关联表
CREATE TABLE `sys_role_permission` (
`role_id` int(11) UNSIGNED NOT NULL,
`permission_id` int(11) UNSIGNED NOT NULL,
PRIMARY KEY (`role_id`,`permission_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 用户-角色关联表
CREATE TABLE `sys_user_role` (
`user_id` int(11) UNSIGNED NOT NULL,
`role_id` int(11) UNSIGNED NOT NULL,
PRIMARY KEY (`user_id`,`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
三、核心服务层实现
3.1 权限验证服务类
<?php
namespace appcommonservice;
use thinkfacadeDb;
use thinkfacadeSession;
class AuthService
{
/**
* 检查用户权限
* @param string $permissionCode 权限标识
* @param int $userId 用户ID
* @return bool
*/
public static function checkPermission(string $permissionCode, int $userId = null): bool
{
if (!$userId) {
$userId = Session::get('user.id');
}
// 获取用户所有角色
$roleIds = Db::name('sys_user_role')
->where('user_id', $userId)
->column('role_id');
if (empty($roleIds)) {
return false;
}
// 获取角色所有权限(包含继承权限)
$allRoleIds = $this->getAllRoleIds($roleIds);
$permission = Db::name('sys_permission')
->where('code', $permissionCode)
->find();
if (!$permission) {
return false;
}
$hasPermission = Db::name('sys_role_permission')
->where('role_id', 'in', $allRoleIds)
->where('permission_id', $permission['id'])
->count();
return $hasPermission > 0;
}
/**
* 获取所有角色ID(包含父级角色)
*/
private function getAllRoleIds(array $roleIds): array
{
$allRoleIds = $roleIds;
do {
$parentRoles = Db::name('sys_role')
->where('id', 'in', $roleIds)
->where('parent_id', '>', 0)
->column('parent_id');
if (!empty($parentRoles)) {
$allRoleIds = array_merge($allRoleIds, $parentRoles);
$roleIds = $parentRoles;
}
} while (!empty($parentRoles));
return array_unique($allRoleIds);
}
/**
* 生成动态菜单树
*/
public function generateMenuTree(int $userId): array
{
$roleIds = Db::name('sys_user_role')
->where('user_id', $userId)
->column('role_id');
$allRoleIds = $this->getAllRoleIds($roleIds);
$permissions = Db::name('sys_permission')
->alias('p')
->join('sys_role_permission rp', 'p.id = rp.permission_id')
->where('rp.role_id', 'in', $allRoleIds)
->where('p.type', 'menu')
->where('p.status', 1)
->order('p.sort', 'asc')
->select()
->toArray();
return $this->buildTree($permissions);
}
/**
* 构建树形结构
*/
private function buildTree(array $items, int $parentId = 0): array
{
$tree = [];
foreach ($items as $item) {
if ($item['parent_id'] == $parentId) {
$children = $this->buildTree($items, $item['id']);
if ($children) {
$item['children'] = $children;
}
$tree[] = $item;
}
}
return $tree;
}
}
四、中间件实现全局权限验证
<?php
namespace appadminmiddleware;
use appcommonserviceAuthService;
use thinkexceptionHttpResponseException;
class AuthCheck
{
public function handle($request, Closure $next)
{
// 1. 排除登录页面
$whiteList = ['/admin/login', '/admin/logout'];
if (in_array($request->pathinfo(), $whiteList)) {
return $next($request);
}
// 2. 检查登录状态
$userId = session('user.id');
if (!$userId) {
return redirect('/admin/login');
}
// 3. 权限验证(自动生成权限标识)
$module = $request->module();
$controller = $request->controller();
$action = $request->action();
// 生成权限标识:module:controller/action
$permissionCode = strtolower("{$module}:{$controller}/{$action}");
// 4. 超级管理员跳过验证
if (!$this->isSuperAdmin($userId)) {
if (!AuthService::checkPermission($permissionCode, $userId)) {
if ($request->isAjax()) {
throw new HttpResponseException(json([
'code' => 403,
'msg' => '没有操作权限',
'data' => null
]));
} else {
abort(403, '没有访问权限');
}
}
}
// 5. 注入菜单数据到视图
$request->menuTree = AuthService::generateMenuTree($userId);
return $next($request);
}
/**
* 检查是否为超级管理员
*/
private function isSuperAdmin(int $userId): bool
{
// 这里可以根据实际情况实现,比如检查特定角色
$superAdminIds = [1]; // 假设ID为1的是超级管理员
return in_array($userId, $superAdminIds);
}
}
在应用配置中注册中间件:
// app/admin/middleware.php
return [
// 全局请求缓存
// thinkmiddlewareCheckRequestCache::class,
// 多语言加载
// thinkmiddlewareLoadLangPack::class,
// Session初始化
thinkmiddlewareSessionInit::class,
// 权限验证中间件
appadminmiddlewareAuthCheck::class,
];
五、控制器层的最佳实践
5.1 基类控制器封装
<?php
namespace appadmincontroller;
use thinkApp;
use thinkfacadeView;
class BaseController extends thinkController
{
protected $service;
public function __construct(App $app)
{
parent::__construct($app);
// 自动注入服务类
$controller = request()->controller();
$serviceClass = "app\admin\service\{$controller}Service";
if (class_exists($serviceClass)) {
$this->service = new $serviceClass();
}
// 共享菜单数据到所有视图
View::assign('menuTree', request()->menuTree ?? []);
}
/**
* 统一成功响应
*/
protected function success($msg = '操作成功', $data = null, $url = null)
{
return json([
'code' => 200,
'msg' => $msg,
'data' => $data,
'url' => $url
]);
}
/**
* 统一失败响应
*/
protected function error($msg = '操作失败', $code = 400, $data = null)
{
return json([
'code' => $code,
'msg' => $msg,
'data' => $data
]);
}
}
5.2 用户控制器示例
<?php
namespace appadmincontroller;
use appadminserviceUserService;
use appcommonvalidateUserValidate;
class User extends BaseController
{
/**
* 用户列表(带权限验证)
*/
public function index()
{
$page = $this->request->param('page', 1);
$limit = $this->request->param('limit', 20);
$search = $this->request->param();
$result = $this->service->getUserList($page, $limit, $search);
if ($this->request->isAjax()) {
return $this->success('获取成功', $result);
}
return view('index', [
'roles' => Db::name('sys_role')->select()
]);
}
/**
* 添加用户
*/
public function add()
{
if ($this->request->isPost()) {
$data = $this->request->post();
// 数据验证
$validate = new UserValidate();
if (!$validate->scene('add')->check($data)) {
return $this->error($validate->getError());
}
// 业务处理
$result = $this->service->createUser($data);
if ($result) {
// 记录操作日志
$this->logOperation('添加用户', $data);
return $this->success('用户添加成功');
}
return $this->error('用户添加失败');
}
return view('add');
}
}
六、前端权限控制实现
6.1 基于Vue.js的动态菜单组件
<template>
<el-menu
:default-active="activeMenu"
mode="vertical"
@select="handleSelect"
>
<template v-for="item in menuTree">
<el-submenu
v-if="item.children && item.children.length"
:key="item.id"
:index="item.code"
>
<template #title>
<i v-if="item.icon" :class="item.icon"></i>
<span>{{ item.name }}</span>
</template>
<menu-item :menu-data="item.children" />
</el-submenu>
<el-menu-item
v-else
:key="item.id"
:index="item.code"
:route="item.path"
>
<i v-if="item.icon" :class="item.icon"></i>
<span>{{ item.name }}</span>
</el-menu-item>
</template>
</el-menu>
</template>
<script>
export default {
name: 'MenuTree',
props: {
menuTree: {
type: Array,
default: () => []
}
},
computed: {
activeMenu() {
const route = this.$route;
const { meta, path } = route;
return meta.activeMenu || path;
}
},
methods: {
handleSelect(index, indexPath) {
// 根据权限标识跳转到对应路由
const permission = this.findPermissionByCode(index);
if (permission && permission.path) {
this.$router.push(permission.path);
}
},
findPermissionByCode(code) {
// 递归查找权限项
const find = (items) => {
for (const item of items) {
if (item.code === code) return item;
if (item.children) {
const found = find(item.children);
if (found) return found;
}
}
return null;
};
return find(this.menuTree);
}
}
};
</script>
6.2 按钮级权限指令
// permission.js
import { checkPermission } from '@/api/auth';
export default {
install(Vue) {
Vue.directive('permission', {
inserted(el, binding) {
const { value } = binding;
if (value && Array.isArray(value)) {
const [permissionCode, action] = value;
// 调用API检查权限
checkPermission(permissionCode).then(hasPermission => {
if (!hasPermission) {
if (action === 'disable') {
el.disabled = true;
el.classList.add('is-disabled');
} else {
el.parentNode && el.parentNode.removeChild(el);
}
}
});
}
}
});
}
};
// 使用示例
// <button v-permission="['user:delete', 'hide']">删除用户</button>
// <button v-permission="['user:edit', 'disable']">编辑用户</button>
七、性能优化与安全加固
7.1 权限缓存策略
<?php
namespace appcommonservice;
use thinkfacadeCache;
class AuthCacheService
{
const CACHE_PREFIX = 'auth:';
const TTL = 3600; // 1小时
/**
* 获取用户权限缓存
*/
public static function getUserPermissions(int $userId): array
{
$cacheKey = self::CACHE_PREFIX . 'user_permissions:' . $userId;
return Cache::remember($cacheKey, function() use ($userId) {
$authService = new AuthService();
$roleIds = Db::name('sys_user_role')
->where('user_id', $userId)
->column('role_id');
$allRoleIds = $authService->getAllRoleIds($roleIds);
return Db::name('sys_permission')
->alias('p')
->join('sys_role_permission rp', 'p.id = rp.permission_id')
->where('rp.role_id', 'in', $allRoleIds)
->column('p.code');
}, self::TTL);
}
/**
* 清除用户权限缓存
*/
public static function clearUserCache(int $userId): void
{
$cacheKey = self::CACHE_PREFIX . 'user_permissions:' . $userId;
Cache::delete($cacheKey);
// 同时清除相关角色的缓存
$roleIds = Db::name('sys_user_role')
->where('user_id', $userId)
->column('role_id');
foreach ($roleIds as $roleId) {
Cache::delete(self::CACHE_PREFIX . 'role_permissions:' . $roleId);
}
}
}
7.2 SQL注入防护与XSS过滤
<?php
namespace appcommontrait;
trait SecurityTrait
{
/**
* 安全过滤输入数据
*/
protected function filterInput(array $data): array
{
$filtered = [];
foreach ($data as $key => $value) {
if (is_array($value)) {
$filtered[$key] = $this->filterInput($value);
} else {
// 去除HTML标签
$value = strip_tags($value);
// 转义特殊字符
$value = htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
// 去除多余空白
$value = trim($value);
$filtered[$key] = $value;
}
}
return $filtered;
}
/**
* 验证ID参数(防止SQL注入)
*/
protected function validateId($id): bool
{
if (is_array($id)) {
foreach ($id as $item) {
if (!ctype_digit((string)$item)) {
return false;
}
}
return true;
}
return ctype_digit((string)$id);
}
}
八、部署与监控
8.1 数据库迁移文件
<?php
// database/migrations/20231001000000_create_rbac_tables.php
use thinkmigrationMigrator;
use thinkmigrationdbColumn;
class CreateRbacTables extends Migrator
{
public function change()
{
// 创建权限表
$table = $this->table('sys_permission', [
'engine' => 'InnoDB',
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'comment' => '系统权限表'
]);
$table->addColumn('name', 'string', ['limit' => 50, 'comment' => '权限名称'])
->addColumn('code', 'string', ['limit' => 100, 'comment' => '权限标识'])
->addColumn('type', 'enum', [
'values' => ['menu', 'button', 'api'],
'default' => 'menu',
'comment' => '权限类型'
])
->addColumn('parent_id', 'integer', ['default' => 0, 'comment' => '父级ID'])
->addColumn('sort', 'integer', ['default' => 0, 'comment' => '排序'])
->addColumn('created_at', 'timestamp', ['default' => 'CURRENT_TIMESTAMP'])
->addColumn('updated_at', 'timestamp', [
'default' => 'CURRENT_TIMESTAMP',
'update' => 'CURRENT_TIMESTAMP'
])
->addIndex(['code'], ['unique' => true])
->addIndex(['parent_id'])
->create();
// 插入默认数据
$this->insertDefaultPermissions();
}
private function insertDefaultPermissions()
{
$data = [
[
'name' => '系统管理',
'code' => 'system',
'type' => 'menu',
'parent_id' => 0,
'sort' => 100
],
// ... 更多默认数据
];
$this->table('sys_permission')->insert($data)->save();
}
}
8.2 性能监控中间件
<?php
namespace appcommonmiddleware;
class PerformanceMonitor
{
public function handle($request, Closure $next)
{
// 记录开始时间
$startTime = microtime(true);
$startMemory = memory_get_usage();
$response = $next($request);
// 计算执行时间和内存使用
$endTime = microtime(true);
$endMemory = memory_get_usage();
$executionTime = round(($endTime - $startTime) * 1000, 2);
$memoryUsage = round(($endMemory - $startMemory) / 1024, 2);
// 记录到日志(仅在生产环境)
if (app()->isDebug()) {
Log::info('Performance Monitor', [
'url' => $request->url(),
'method' => $request->method(),
'execution_time' => $executionTime . 'ms',
'memory_usage' => $memoryUsage . 'KB',
'ip' => $request->ip(),
'user_agent' => $request->header('user-agent')
]);
}
// 添加响应头(用于监控系统)
$response->header([
'X-Execution-Time' => $executionTime . 'ms',
'X-Memory-Usage' => $memoryUsage . 'KB'
]);
return $response;
}
}
九、总结与最佳实践
9.1 架构优势
- 模块化设计:采用多应用模式,前后端分离清晰
- 权限粒度控制:支持菜单、按钮、API三级权限控制
- 性能优化:多层缓存策略,减少数据库查询
- 安全加固:完整的输入验证和XSS防护
- 可扩展性:支持角色继承和多级权限管理
9.2 部署建议
- 生产环境建议使用Redis作为缓存驱动
- 定期清理无效的权限缓存
- 使用数据库迁移工具管理表结构变更
- 开启OPcache提升PHP执行效率
- 配置合适的Session存储机制(推荐Redis)
9.3 扩展方向
本系统可进一步扩展为:
- 数据权限控制(基于部门、岗位的数据过滤)
- 操作日志审计系统
- 单点登录(SSO)集成
- 第三方OAuth2.0授权
- 微服务架构改造

