ThinkPHP 6.0 模块化开发实战:构建企业级权限管理系统核心架构

2026-02-07 0 809
免费资源下载

作者:技术架构师 | 发布日期: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 部署建议

  1. 生产环境建议使用Redis作为缓存驱动
  2. 定期清理无效的权限缓存
  3. 使用数据库迁移工具管理表结构变更
  4. 开启OPcache提升PHP执行效率
  5. 配置合适的Session存储机制(推荐Redis)

9.3 扩展方向

本系统可进一步扩展为:

  • 数据权限控制(基于部门、岗位的数据过滤)
  • 操作日志审计系统
  • 单点登录(SSO)集成
  • 第三方OAuth2.0授权
  • 微服务架构改造
ThinkPHP 6.0 模块化开发实战:构建企业级权限管理系统核心架构
收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

淘吗网 thinkphp ThinkPHP 6.0 模块化开发实战:构建企业级权限管理系统核心架构 https://www.taomawang.com/server/thinkphp/1586.html

常见问题

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务