ThinkPHP项目用户权限RBAC设计

wen PHP项目 8

本文目录导读:

ThinkPHP项目用户权限RBAC设计

  1. 数据库设计
  2. 模型创建
  3. 中间件实现
  4. 认证服务类
  5. 控制器示例
  6. 路由配置
  7. 视图模板示例
  8. 初始化数据
  9. 扩展功能建议
  10. 安全建议

我来为您设计一个完整的ThinkPHP用户权限RBAC系统。

数据库设计

数据表结构

-- 用户表
CREATE TABLE `tp_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) NOT NULL COMMENT '用户名',
  `password` varchar(255) NOT NULL COMMENT '密码',
  `real_name` varchar(50) DEFAULT NULL COMMENT '真实姓名',
  `email` varchar(100) DEFAULT NULL COMMENT '邮箱',
  `phone` varchar(20) DEFAULT NULL COMMENT '手机号',
  `status` tinyint(1) DEFAULT '1' COMMENT '状态:1启用 0禁用',
  `last_login_time` datetime DEFAULT NULL COMMENT '最后登录时间',
  `last_login_ip` varchar(50) DEFAULT NULL COMMENT '最后登录IP',
  `create_time` datetime DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
-- 角色表
CREATE TABLE `tp_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL COMMENT '角色名称',
  `code` varchar(50) NOT NULL COMMENT '角色标识',
  `description` varchar(255) DEFAULT NULL COMMENT '角色描述',
  `status` tinyint(1) DEFAULT '1' COMMENT '状态:1启用 0禁用',
  `create_time` datetime DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `code` (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色表';
-- 权限表(菜单/节点)
CREATE TABLE `tp_permission` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `parent_id` int(11) DEFAULT '0' COMMENT '父权限ID',
  `name` varchar(50) NOT NULL COMMENT '权限名称',
  `type` tinyint(1) DEFAULT '1' COMMENT '类型:1菜单 2按钮 3API',
  `url` varchar(255) DEFAULT NULL COMMENT '路由地址',
  `icon` varchar(50) DEFAULT NULL COMMENT '图标',
  `sort` int(11) DEFAULT '0' COMMENT '排序',
  `status` tinyint(1) DEFAULT '1' COMMENT '状态:1启用 0禁用',
  `create_time` datetime DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='权限表';
-- 用户角色关联表
CREATE TABLE `tp_user_role` (
  `user_id` int(11) NOT NULL,
  `role_id` int(11) NOT NULL,
  PRIMARY KEY (`user_id`,`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户角色关联表';
-- 角色权限关联表
CREATE TABLE `tp_role_permission` (
  `role_id` int(11) NOT NULL,
  `permission_id` int(11) NOT NULL,
  PRIMARY KEY (`role_id`,`permission_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色权限关联表';

模型创建

用户模型

<?php
// app/model/User.php
namespace app\model;
use think\Model;
use think\model\concern\SoftDelete;
class User extends Model
{
    use SoftDelete;
    protected $deleteTime = 'delete_time';
    // 关联角色
    public function roles()
    {
        return $this->belongsToMany(Role::class, 'user_role', 'role_id', 'user_id');
    }
    // 密码加密
    public function setPasswordAttr($value)
    {
        return password_hash($value, PASSWORD_BCRYPT);
    }
    // 验证密码
    public function verifyPassword($password)
    {
        return password_verify($password, $this->password);
    }
    // 获取用户所有权限
    public function getPermissions()
    {
        $permissionIds = [];
        $roles = $this->roles()->where('status', 1)->select();
        foreach ($roles as $role) {
            $permissions = $role->permissions()->where('status', 1)->select();
            foreach ($permissions as $permission) {
                $permissionIds[$permission->url] = true;
            }
        }
        return array_keys($permissionIds);
    }
    // 检查权限
    public function hasPermission($permissionUrl)
    {
        $permissions = $this->getPermissions();
        return in_array($permissionUrl, $permissions);
    }
    // 检查角色
    public function hasRole($roleCode)
    {
        $roles = $this->roles()->where('code', $roleCode)->find();
        return $roles ? true : false;
    }
}

角色模型

<?php
// app/model/Role.php
namespace app\model;
use think\Model;
use think\model\concern\SoftDelete;
class Role extends Model
{
    use SoftDelete;
    // 关联用户
    public function users()
    {
        return $this->belongsToMany(User::class, 'user_role', 'user_id', 'role_id');
    }
    // 关联权限
    public function permissions()
    {
        return $this->belongsToMany(Permission::class, 'role_permission', 'permission_id', 'role_id');
    }
    // 添加角色并绑定用户
    public static function createRole($data, $userIds = [])
    {
        $role = self::create($data);
        if (!empty($userIds)) {
            $role->users()->attach($userIds);
        }
        return $role;
    }
}

权限模型

<?php
// app/model/Permission.php
namespace app\model;
use think\Model;
class Permission extends Model
{
    // 关联角色
    public function roles()
    {
        return $this->belongsToMany(Role::class, 'role_permission', 'role_id', 'permission_id');
    }
    // 获取树形权限
    public static function getTree($parentId = 0)
    {
        $permissions = self::where('parent_id', $parentId)
            ->order('sort', 'asc')
            ->select()
            ->toArray();
        foreach ($permissions as &$permission) {
            $children = self::getTree($permission['id']);
            if (!empty($children)) {
                $permission['children'] = $children;
            }
        }
        return $permissions;
    }
}

中间件实现

<?php
// app/middleware/AuthCheck.php
namespace app\middleware;
use think\facade\Session;
use think\facade\Url;
class AuthCheck
{
    public function handle($request, \Closure $next)
    {
        // 检查是否登录
        if (!Session::has('user_id')) {
            if ($request->isAjax()) {
                return json(['code' => 401, 'msg' => '请先登录']);
            }
            return redirect((string)Url::build('login/index'));
        }
        // 获取当前请求的模块、控制器、方法
        $module = app('http')->getName();
        $controller = $request->controller();
        $action = $request->action();
        // 构建权限标识
        $permission = "{$module}/{$controller}/{$action}";
        // 超级管理员跳过验证
        $user = \app\model\User::find(Session::get('user_id'));
        if ($user->hasRole('admin')) {
            return $next($request);
        }
        // 检查权限
        if (!$user->hasPermission($permission)) {
            if ($request->isAjax()) {
                return json(['code' => 403, 'msg' => '没有权限']);
            }
            abort(403, '没有访问权限');
        }
        return $next($request);
    }
}

认证服务类

<?php
// app/service/AuthService.php
namespace app\service;
use app\model\User;
use app\model\Role;
use app\model\Permission;
use think\facade\Session;
use think\facade\Cache;
class AuthService
{
    /**
     * 登录认证
     */
    public function login($username, $password)
    {
        $user = User::where('username', $username)
            ->where('status', 1)
            ->find();
        if (!$user) {
            return ['code' => 0, 'msg' => '用户不存在或已被禁用'];
        }
        if (!$user->verifyPassword($password)) {
            return ['code' => 0, 'msg' => '密码错误'];
        }
        // 更新登录信息
        $user->last_login_time = date('Y-m-d H:i:s');
        $user->last_login_ip = request()->ip();
        $user->save();
        // 设置Session
        Session::set('user_id', $user->id);
        Session::set('user_info', $user->toArray());
        // 缓存权限
        $this->cacheUserPermissions($user->id);
        return ['code' => 1, 'msg' => '登录成功', 'user' => $user];
    }
    /**
     * 缓存用户权限
     */
    public function cacheUserPermissions($userId)
    {
        $user = User::find($userId);
        $permissions = $user->getPermissions();
        Cache::set("user_permissions_{$userId}", $permissions, 120);
        return $permissions;
    }
    /**
     * 退出登录
     */
    public function logout()
    {
        $userId = Session::get('user_id');
        Cache::delete("user_permissions_{$userId}");
        Session::clear();
    }
    /**
     * 获取当前用户信息
     */
    public function getUserInfo()
    {
        $userId = Session::get('user_id');
        if (!$userId) {
            return null;
        }
        $user = User::with(['roles'])
            ->find($userId);
        return $user;
    }
    /**
     * 生成权限树
     */
    public function getPermissionTree()
    {
        $user = $this->getUserInfo();
        if (!$user) {
            return [];
        }
        // 超级管理员获取所有权限
        if ($user->hasRole('admin')) {
            $permissions = Permission::where('type', 1)
                ->order('sort', 'asc')
                ->select()
                ->toArray();
        } else {
            // 普通用户获取自己的权限菜单
            $roleIds = $user->roles()->column('id');
            $permissionIds = \think\facade\Db::name('role_permission')
                ->whereIn('role_id', $roleIds)
                ->column('permission_id');
            $permissions = Permission::whereIn('id', $permissionIds)
                ->where('type', 1)
                ->where('status', 1)
                ->order('sort', 'asc')
                ->select()
                ->toArray();
        }
        return $this->buildTree($permissions);
    }
    /**
     * 构建树形结构
     */
    private function buildTree($data, $parentId = 0)
    {
        $tree = [];
        foreach ($data as $item) {
            if ($item['parent_id'] == $parentId) {
                $children = $this->buildTree($data, $item['id']);
                if ($children) {
                    $item['children'] = $children;
                }
                $tree[] = $item;
            }
        }
        return $tree;
    }
}

控制器示例

<?php
// app/controller/Login.php
namespace app\controller;
use think\Request;
use app\service\AuthService;
class Login
{
    protected $authService;
    public function __construct(AuthService $authService)
    {
        $this->authService = $authService;
    }
    // 显示登录页面
    public function index()
    {
        return view('login/index');
    }
    // 处理登录
    public function login(Request $request)
    {
        $username = $request->post('username');
        $password = $request->post('password');
        $result = $this->authService->login($username, $password);
        if ($result['code'] == 1) {
            return json(['code' => 1, 'msg' => '登录成功', 'url' => '/index']);
        }
        return json(['code' => 0, 'msg' => $result['msg']]);
    }
    // 退出登录
    public function logout()
    {
        $this->authService->logout();
        return redirect('/login');
    }
}
<?php
// app/controller/User.php
namespace app\controller;
use app\model\User;
use app\model\Role;
use think\Request;
class User extends Base
{
    // 用户列表
    public function index()
    {
        $users = User::with('roles')->paginate(10);
        return view('user/index', ['users' => $users]);
    }
    // 添加用户
    public function add()
    {
        if (request()->isPost()) {
            $data = request()->post();
            $roles = $data['roles'] ?? [];
            unset($data['roles']);
            $user = User::create($data);
            if (!empty($roles)) {
                $user->roles()->attach($roles);
            }
            return json(['code' => 1, 'msg' => '添加成功']);
        }
        $roles = Role::where('status', 1)->select();
        return view('user/add', ['roles' => $roles]);
    }
    // 分配角色
    public function assignRole($userId)
    {
        if (request()->isPost()) {
            $roleIds = request()->post('roles', []);
            $user = User::find($userId);
            $user->roles()->detach();
            if (!empty($roleIds)) {
                $user->roles()->attach($roleIds);
            }
            // 清除权限缓存
            \think\facade\Cache::delete("user_permissions_{$userId}");
            return json(['code' => 1, 'msg' => '分配成功']);
        }
        $user = User::with('roles')->find($userId);
        $roles = Role::where('status', 1)->select();
        return view('user/assign_role', [
            'user' => $user,
            'roles' => $roles
        ]);
    }
}

路由配置

<?php
// route/app.php
use think\facade\Route;
// 登录相关
Route::get('login', 'Login/index');
Route::post('login', 'Login/login');
Route::get('logout', 'Login/logout');
// 需要认证的路由
Route::group(function () {
    // 首页
    Route::get('/', 'Index/index');
    Route::get('index', 'Index/index');
    // 用户管理
    Route::group('user', function () {
        Route::get('/', 'User/index');
        Route::get('add', 'User/add');
        Route::post('add', 'User/add');
        Route::get('edit/:id', 'User/edit');
        Route::post('edit', 'User/edit');
        Route::post('delete/:id', 'User/delete');
        Route::get('assign/:id', 'User/assignRole');
        Route::post('assign', 'User/assignRole');
    });
    // 角色管理
    Route::group('role', function () {
        Route::get('/', 'Role/index');
        Route::get('add', 'Role/add');
        Route::post('add', 'Role/add');
        Route::post('delete/:id', 'Role/delete');
    });
})->middleware(\app\middleware\AuthCheck::class);

视图模板示例

<!-- app/view/layout/main.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">{$title|default='管理系统'}</title>
    <link rel="stylesheet" href="/static/css/style.css">
</head>
<body>
    <div class="layout">
        <div class="sidebar">
            <ul>
                {foreach $menus as $menu}
                    {if isset($menu['children'])}
                    <li class="has-sub">
                        <a href="javascript:;">{$menu.name}</a>
                        <ul>
                            {foreach $menu['children'] as $child}
                            <li><a href="{$child.url}">{$child.name}</a></li>
                            {/foreach}
                        </ul>
                    </li>
                    {else}
                    <li><a href="{$menu.url}">{$menu.name}</a></li>
                    {/if}
                {/foreach}
            </ul>
        </div>
        <div class="main">
            {__CONTENT__}
        </div>
    </div>
</body>
</html>

初始化数据

<?php
// database/seed/RbacSeeder.php
namespace database\seed;
use think\Db;
class RbacSeeder
{
    public function run()
    {
        // 创建超级管理员角色
        $adminRoleId = Db::name('role')->insertGetId([
            'name' => '超级管理员',
            'code' => 'admin',
            'description' => '拥有所有权限',
            'status' => 1,
            'create_time' => date('Y-m-d H:i:s')
        ]);
        // 创建默认用户
        $userId = Db::name('user')->insertGetId([
            'username' => 'admin',
            'password' => password_hash('123456', PASSWORD_BCRYPT),
            'real_name' => '管理员',
            'status' => 1,
            'create_time' => date('Y-m-d H:i:s')
        ]);
        // 绑定用户角色
        Db::name('user_role')->insert([
            'user_id' => $userId,
            'role_id' => $adminRoleId
        ]);
        // 创建权限数据
        $permissions = [
            ['name' => '系统管理', 'parent_id' => 0, 'url' => '/system', 'sort' => 1],
            ['name' => '用户管理', 'parent_id' => 1, 'url' => '/user', 'sort' => 1],
            ['name' => '角色管理', 'parent_id' => 1, 'url' => '/role', 'sort' => 2],
            ['name' => '权限管理', 'parent_id' => 1, 'url' => '/permission', 'sort' => 3],
        ];
        foreach ($permissions as $permission) {
            $permissionId = Db::name('permission')->insertGetId($permission);
            // 为超级管理员分配所有权限
            Db::name('role_permission')->insert([
                'role_id' => $adminRoleId,
                'permission_id' => $permissionId
            ]);
        }
    }
}

扩展功能建议

  1. 多级权限控制: 支持菜单、按钮、API级别的权限控制
  2. 权限缓存: 使用Redis缓存用户权限,提高性能
  3. 操作日志: 记录用户关键操作日志
  4. 数据权限: 扩展实现数据级别的权限控制
  5. 登录限流: 防止暴力破解
  6. 单点登录: 同一账号仅允许一处登录
  7. 权限动态更新: 管理员修改权限后自动刷新所有用户缓存

安全建议

  1. 密码必须加密存储
  2. Token认证 + Session双机制
  3. CSRF防护
  4. SQL注入防护
  5. XSS攻击防护
  6. 访问频率限制
  7. 日志审计系统

这个RBAC系统设计完整,可以满足大部分中小型项目的权限管理需求,可以根据实际需求调整功能,比如增加数据权限、组织架构等。

抱歉,评论功能暂时关闭!