ThinkPHP多层级权限管理系统实现教程 – RBAC权限控制与最佳实践

2025-08-26 0 830

权限管理是Web应用开发中的核心需求之一。本文将详细介绍如何使用ThinkPHP 6.x构建一个完整的基于RBAC(基于角色的访问控制)的多层级权限管理系统。

系统设计思路

我们将设计一个包含用户、角色、权限组和权限节点的四级权限管理系统:

  • 用户(User):系统的使用者
  • 角色(Role):权限的集合,用户可拥有多个角色
  • 权限组(Group):权限节点的分类
  • 权限节点(Node):具体的操作权限

数据库设计

1. 用户表 (user)

CREATE TABLE `user` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `username` varchar(50) NOT NULL DEFAULT '' COMMENT '用户名',
  `password` varchar(255) NOT NULL DEFAULT '' COMMENT '密码',
  `nickname` varchar(50) NOT NULL DEFAULT '' COMMENT '昵称',
  `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态:1正常,0禁用',
  `create_time` int(11) NOT NULL DEFAULT '0' COMMENT '创建时间',
  `update_time` int(11) NOT NULL DEFAULT '0' COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
    

2. 角色表 (role)

CREATE TABLE `role` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL DEFAULT '' COMMENT '角色名称',
  `description` varchar(255) NOT NULL DEFAULT '' COMMENT '角色描述',
  `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态:1正常,0禁用',
  `create_time` int(11) NOT NULL DEFAULT '0' COMMENT '创建时间',
  `update_time` int(11) NOT NULL DEFAULT '0' COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色表';
    

3. 用户角色关联表 (user_role)

CREATE TABLE `user_role` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL DEFAULT '0' COMMENT '用户ID',
  `role_id` int(11) NOT NULL DEFAULT '0' COMMENT '角色ID',
  `create_time` int(11) NOT NULL DEFAULT '0' COMMENT '创建时间',
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`),
  KEY `role_id` (`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户角色关联表';
    

4. 权限节点表 (auth_node)

CREATE TABLE `auth_node` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL DEFAULT '' COMMENT '节点名称',
  `title` varchar(50) NOT NULL DEFAULT '' COMMENT '节点标题',
  `type` tinyint(1) NOT NULL DEFAULT '1' COMMENT '节点类型:1模块,2控制器,3操作',
  `pid` int(11) NOT NULL DEFAULT '0' COMMENT '父级ID',
  `path` varchar(100) NOT NULL DEFAULT '' COMMENT '节点路径',
  `icon` varchar(50) NOT NULL DEFAULT '' COMMENT '图标',
  `sort` int(11) NOT NULL DEFAULT '0' COMMENT '排序',
  `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态:1正常,0禁用',
  `create_time` int(11) NOT NULL DEFAULT '0' COMMENT '创建时间',
  `update_time` int(11) NOT NULL DEFAULT '0' COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `pid` (`pid`),
  KEY `status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='权限节点表';
    

5. 角色权限关联表 (role_auth)

CREATE TABLE `role_auth` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `role_id` int(11) NOT NULL DEFAULT '0' COMMENT '角色ID',
  `node_id` int(11) NOT NULL DEFAULT '0' COMMENT '节点ID',
  `create_time` int(11) NOT NULL DEFAULT '0' COMMENT '创建时间',
  PRIMARY KEY (`id`),
  KEY `role_id` (`role_id`),
  KEY `node_id` (`node_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色权限关联表';
    

模型设计

1. 用户模型 (User.php)

<?php
namespace appmodel;

use thinkModel;
use thinkmodelconcernSoftDelete;

class User extends Model
{
    use SoftDelete;
    
    protected $name = 'user';
    protected $autoWriteTimestamp = true;
    
    // 用户角色关联
    public function roles()
    {
        return $this->belongsToMany(Role::class, UserRole::class);
    }
    
    // 密码加密
    public function setPasswordAttr($value)
    {
        return password_hash($value, PASSWORD_DEFAULT);
    }
    
    // 验证密码
    public function verifyPassword($password)
    {
        return password_verify($password, $this->password);
    }
    
    // 检查用户是否有某个权限
    public function hasAuth($node)
    {
        // 超级管理员拥有所有权限
        if ($this->id == 1) {
            return true;
        }
        
        $roles = $this->roles()->where('status', 1)->select();
        
        foreach ($roles as $role) {
            if ($role->hasAuth($node)) {
                return true;
            }
        }
        
        return false;
    }
}
?>
    

2. 角色模型 (Role.php)

<?php
namespace appmodel;

use thinkModel;
use thinkmodelconcernSoftDelete;

class Role extends Model
{
    use SoftDelete;
    
    protected $name = 'role';
    protected $autoWriteTimestamp = true;
    
    // 角色权限关联
    public function nodes()
    {
        return $this->belongsToMany(AuthNode::class, RoleAuth::class);
    }
    
    // 检查角色是否有某个权限
    public function hasAuth($node)
    {
        $node = AuthNode::where('name', $node)->find();
        if (!$node) {
            return false;
        }
        
        return $this->nodes()->where('node_id', $node->id)->find() ? true : false;
    }
}
?>
    

3. 权限节点模型 (AuthNode.php)

<?php
namespace appmodel;

use thinkModel;

class AuthNode extends Model
{
    protected $name = 'auth_node';
    protected $autoWriteTimestamp = true;
    
    // 获取所有节点树形结构
    public static function getTree($pid = 0)
    {
        $nodes = self::where('pid', $pid)
            ->where('status', 1)
            ->order('sort asc, id asc')
            ->select();
        
        if ($nodes->isEmpty()) {
            return [];
        }
        
        foreach ($nodes as &$node) {
            $node['children'] = self::getTree($node['id']);
        }
        
        return $nodes->toArray();
    }
    
    // 获取用户有权限的菜单
    public static function getMenu($userId)
    {
        // 超级管理员获取所有菜单
        if ($userId == 1) {
            return self::getTree();
        }
        
        // 获取用户所有角色
        $user = User::find($userId);
        $roles = $user->roles()->where('status', 1)->column('role.id');
        
        if (empty($roles)) {
            return [];
        }
        
        // 获取角色所有权限节点
        $nodeIds = RoleAuth::where('role_id', 'in', $roles)
            ->column('node_id');
        
        if (empty($nodeIds)) {
            return [];
        }
        
        // 获取菜单节点
        $menuNodes = self::where('id', 'in', $nodeIds)
            ->where('status', 1)
            ->where('type', 'in', [1, 2]) // 只获取模块和控制器级别的节点
            ->order('sort asc, id asc')
            ->select();
        
        return self::buildMenuTree($menuNodes->toArray());
    }
    
    // 构建菜单树
    protected static function buildMenuTree($nodes, $pid = 0)
    {
        $tree = [];
        
        foreach ($nodes as $node) {
            if ($node['pid'] == $pid) {
                $children = self::buildMenuTree($nodes, $node['id']);
                if ($children) {
                    $node['children'] = $children;
                }
                $tree[] = $node;
            }
        }
        
        return $tree;
    }
}
?>
    

中间件实现权限验证

权限验证中间件 (Auth.php)

<?php
namespace appmiddleware;

use thinkfacadeRequest;
use thinkfacadeSession;

class Auth
{
    public function handle($request, Closure $next)
    {
        // 排除登录页面和登录操作
        $allowUrl = [
            'admin/index/login',
            'admin/index/dologin',
            'admin/index/logout',
            'admin/index/captcha'
        ];
        
        $currentUrl = strtolower($request->controller() . '/' . $request->action());
        
        if (in_array($currentUrl, $allowUrl)) {
            return $next($request);
        }
        
        // 检查是否登录
        $user = Session::get('admin_user');
        if (!$user) {
            return redirect('/admin/index/login');
        }
        
        // 检查权限
        $node = strtolower($request->module() . '/' . $request->controller() . '/' . $request->action());
        
        if (!$user->hasAuth($node)) {
            if ($request->isAjax()) {
                return json(['code' => 403, 'msg' => '没有权限操作']);
            } else {
                return view('admin@public/error', ['msg' => '没有权限操作']);
            }
        }
        
        return $next($request);
    }
}
?>
    

控制器实现

1. 登录控制器 (Login.php)

<?php
namespace appadmincontroller;

use thinkfacadeRequest;
use thinkfacadeSession;
use thinkfacadeView;
use appmodelUser;

class Login
{
    // 显示登录页面
    public function index()
    {
        return View::fetch();
    }
    
    // 处理登录
    public function doLogin()
    {
        $username = Request::param('username');
        $password = Request::param('password');
        $captcha = Request::param('captcha');
        
        // 验证码验证
        if (!captcha_check($captcha)) {
            return json(['code' => 0, 'msg' => '验证码错误']);
        }
        
        // 查找用户
        $user = User::where('username', $username)->find();
        if (!$user) {
            return json(['code' => 0, 'msg' => '用户不存在']);
        }
        
        if ($user->status != 1) {
            return json(['code' => 0, 'msg' => '用户已被禁用']);
        }
        
        // 验证密码
        if (!$user->verifyPassword($password)) {
            return json(['code' => 0, 'msg' => '密码错误']);
        }
        
        // 登录成功
        Session::set('admin_user', $user);
        
        return json(['code' => 1, 'msg' => '登录成功', 'url' => url('/admin')]);
    }
    
    // 退出登录
    public function logout()
    {
        Session::delete('admin_user');
        return redirect('/admin/index/login');
    }
    
    // 生成验证码
    public function captcha()
    {
        return captcha();
    }
}
?>
    

2. 权限管理控制器 (Auth.php)

<?php
namespace appadmincontroller;

use thinkfacadeRequest;
use thinkfacadeView;
use appmodelAuthNode;
use appmodelRole;

class Auth extends Base
{
    // 节点管理
    public function node()
    {
        if (Request::isAjax()) {
            $list = AuthNode::getTree();
            return json(['code' => 0, 'data' => $list, 'count' => count($list)]);
        }
        
        return View::fetch();
    }
    
    // 添加节点
    public function addNode()
    {
        if (Request::isPost()) {
            $data = Request::post();
            
            $node = new AuthNode();
            $result = $node->save($data);
            
            if ($result) {
                return json(['code' => 1, 'msg' => '添加成功']);
            } else {
                return json(['code' => 0, 'msg' => '添加失败']);
            }
        }
        
        // 获取父级节点
        $parentNodes = AuthNode::where('pid', 0)
            ->where('status', 1)
            ->select();
            
        View::assign('parentNodes', $parentNodes);
        return View::fetch();
    }
    
    // 角色管理
    public function role()
    {
        if (Request::isAjax()) {
            $page = Request::param('page', 1);
            $limit = Request::param('limit', 15);
            
            $list = Role::where('status', 1)
                ->page($page, $limit)
                ->select();
                
            $total = Role::where('status', 1)->count();
            
            return json(['code' => 0, 'data' => $list, 'count' => $total]);
        }
        
        return View::fetch();
    }
    
    // 设置角色权限
    public function setRoleAuth()
    {
        $roleId = Request::param('id');
        $role = Role::find($roleId);
        
        if (Request::isPost()) {
            $nodeIds = Request::post('node_ids/a', []);
            
            // 删除原有权限
            appmodelRoleAuth::where('role_id', $roleId)->delete();
            
            // 添加新权限
            $data = [];
            foreach ($nodeIds as $nodeId) {
                $data[] = [
                    'role_id' => $roleId,
                    'node_id' => $nodeId,
                    'create_time' => time()
                ];
            }
            
            if (!empty($data)) {
                (new appmodelRoleAuth())->saveAll($data);
            }
            
            return json(['code' => 1, 'msg' => '权限设置成功']);
        }
        
        // 获取所有节点
        $allNodes = AuthNode::where('status', 1)
            ->order('sort asc, id asc')
            ->select();
            
        // 获取角色已有权限
        $roleNodes = $role->nodes()->column('node_id');
        
        View::assign([
            'role' => $role,
            'allNodes' => $allNodes,
            'roleNodes' => $roleNodes
        ]);
        
        return View::fetch();
    }
}
?>
    

视图模板示例

1. 后台主模板 (layout.html)

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>{$title|default='后台管理系统'}</title>
    <link rel="stylesheet" href="/static/admin/css/layui.css" rel="external nofollow" >
    <link rel="stylesheet" href="/static/admin/css/admin.css" rel="external nofollow" >
</head>
<body class="layui-layout-body">
    <div class="layui-layout layui-layout-admin">
        <div class="layui-header">
            <div class="layui-logo">后台管理系统</div>
            <ul class="layui-nav layui-layout-right">
                <li class="layui-nav-item">
                    <a href="javascript:;" rel="external nofollow"  rel="external nofollow" >
                        <img src="/static/admin/images/avatar.jpg" class="layui-nav-img">
                        {$admin_user.nickname}
                    </a>
                    <dl class="layui-nav-child">
                        <dd><a href="{:url('admin/profile/index')}" rel="external nofollow" >基本资料</a></dd>
                        <dd><a href="{:url('admin/index/logout')}" rel="external nofollow" >退出</a></dd>
                    </dl>
                </li>
            </ul>
        </div>
        
        <div class="layui-side layui-bg-black">
            <div class="layui-side-scroll">
                <ul class="layui-nav layui-nav-tree" lay-filter="admin-menu">
                    {volist name="menu" id="vo"}
                    <li class="layui-nav-item {notempty name="vo.children"}layui-nav-itemed{/notempty}">
                        <a href="javascript:;" rel="external nofollow"  rel="external nofollow" ><i class="{$vo.icon}"></i> <cite>{$vo.title}</cite></a>
                        {notempty name="vo.children"}
                        <dl class="layui-nav-child">
                            {volist name="vo.children" id="child"}
                            <dd>
                                <a href="{:url($child.name)}" rel="external nofollow" ><i class="{$child.icon}"></i> <cite>{$child.title}</cite></a>
                            </dd>
                            {/volist}
                        </dl>
                        {/notempty}
                    </li>
                    {/volist}
                </ul>
            </div>
        </div>
        
        <div class="layui-body">
            <div class="layui-card">
                <div class="layui-card-header">{$title|default='后台管理'}</div>
                <div class="layui-card-body">
                    {__CONTENT__}
                </div>
            </div>
        </div>
        
        <div class="layui-footer">
            © 2023 后台管理系统
        </div>
    </div>
    
    <script src="/static/admin/layui.js"></script>
    <script>
    layui.use(['element', 'layer'], function(){
        var element = layui.element;
        var layer = layui.layer;
    });
    </script>
</body>
</html>
    

权限验证流程

  1. 用户访问受保护的路由
  2. Auth中间件检查用户是否登录
  3. 如果未登录,重定向到登录页面
  4. 如果已登录,获取当前访问的节点(模块/控制器/方法)
  5. 检查用户是否有该节点的访问权限
  6. 如果有权限,继续执行请求
  7. 如果没有权限,返回权限不足提示

系统特点

  • 多层级权限控制:支持模块→控制器→操作的三级权限控制
  • 灵活的权限分配:可以为角色分配任意权限组合
  • 用户多角色支持:一个用户可以拥有多个角色,权限取并集
  • 菜单动态生成:根据用户权限动态生成侧边栏菜单
  • 中间件验证:使用中间件统一处理权限验证逻辑
  • ThinkPHP最佳实践:充分利用ThinkPHP的模型关联、软删除等特性

扩展建议

这个权限管理系统可以进一步扩展:

  1. 添加操作日志记录功能,记录用户的关键操作
  2. 实现数据权限控制,根据不同角色限制数据访问范围
  3. 添加部门管理功能,实现组织架构级别的权限控制
  4. 集成前端Vue.js等框架,实现前后端分离架构
  5. 添加API接口权限验证,支持移动端应用
  6. 实现权限变更的审批流程,提高系统安全性

总结

本文详细介绍了如何使用ThinkPHP 6.x构建一个完整的RBAC权限管理系统。这个系统包含了用户管理、角色管理、权限节点管理和权限分配等核心功能,采用了中间件进行统一的权限验证,确保了系统的安全性和可扩展性。

通过这个案例,您可以学习到ThinkPHP的模型关联、中间件、软删除等高级特性的实际应用,以及如何设计一个健壮的权限管理系统。这个系统可以作为各种后台管理系统的基础框架,根据实际需求进行扩展和定制。

ThinkPHP多层级权限管理系统实现教程 - RBAC权限控制与最佳实践
收藏 (0) 打赏

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

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

淘吗网 thinkphp ThinkPHP多层级权限管理系统实现教程 – RBAC权限控制与最佳实践 https://www.taomawang.com/server/thinkphp/984.html

常见问题

相关文章

发表评论
暂无评论
官方客服团队

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