发布日期:2024年1月18日 | 作者:ThinkPHP框架专家
本教程将深入讲解如何使用ThinkPHP 6.x框架开发一个完整的企业级RBAC(基于角色的权限控制)管理系统。
一、项目概述与技术栈选择
系统功能需求
- 多角色用户权限管理
- 动态菜单权限控制
- 操作日志记录与审计
- 数据权限隔离
- API接口安全验证
技术架构选型
后端框架:ThinkPHP 6.1.x
数据库:MySQL 8.0
缓存:Redis 6.x
前端:LayUI + jQuery
API文档:Swagger
部署:Docker + Nginx
二、环境配置与框架安装
Composer安装ThinkPHP 6.x
# 创建新项目
composer create-project topthink/think tp-rbac-system
# 进入项目目录
cd tp-rbac-system
# 安装扩展依赖
composer require topthink/think-multi-app
composer require topthink/think-migration
多应用模式配置
// config/app.php
return [
// 开启多应用模式
'auto_multi_app' => true,
'app_express' => true,
// 默认应用
'default_app' => 'admin',
// 默认时区
'default_timezone' => 'Asia/Shanghai',
];
目录结构规划
tp-rbac-system/
├── app/
│ ├── admin/ # 后台管理应用
│ ├── api/ # API接口应用
│ └── common/ # 公共模块
├── config/
├── public/
├── runtime/
└── vendor/
三、数据库设计与模型创建
RBAC核心表结构
-- 用户表
CREATE TABLE `rbac_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL COMMENT '用户名',
`password` varchar(255) NOT NULL COMMENT '密码',
`realname` varchar(50) DEFAULT NULL COMMENT '真实姓名',
`email` varchar(100) DEFAULT NULL COMMENT '邮箱',
`mobile` varchar(20) DEFAULT NULL COMMENT '手机号',
`status` tinyint(1) DEFAULT '1' COMMENT '状态:1正常 0禁用',
`last_login_time` datetime DEFAULT NULL COMMENT '最后登录时间',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
-- 角色表
CREATE TABLE `rbac_role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL COMMENT '角色名称',
`description` varchar(255) DEFAULT NULL COMMENT '角色描述',
`status` tinyint(1) DEFAULT '1' COMMENT '状态:1正常 0禁用',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色表';
-- 权限节点表
CREATE TABLE `rbac_node` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`pid` int(11) DEFAULT '0' COMMENT '父级ID',
`name` varchar(50) NOT NULL COMMENT '节点名称',
`path` varchar(100) DEFAULT NULL COMMENT '节点路径',
`type` tinyint(1) DEFAULT '1' COMMENT '类型:1菜单 2操作',
`icon` varchar(50) DEFAULT NULL COMMENT '图标',
`sort` int(11) DEFAULT '0' COMMENT '排序',
`status` tinyint(1) DEFAULT '1' COMMENT '状态',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='权限节点表';
-- 用户角色关联表
CREATE TABLE `rbac_user_role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL COMMENT '用户ID',
`role_id` int(11) NOT NULL COMMENT '角色ID',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `user_role` (`user_id`,`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户角色关联表';
-- 角色权限关联表
CREATE TABLE `rbac_role_node` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`role_id` int(11) NOT NULL COMMENT '角色ID',
`node_id` int(11) NOT NULL COMMENT '权限节点ID',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `role_node` (`role_id`,`node_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色权限关联表';
模型类创建
<?php
// app/admin/model/User.php
namespace appadminmodel;
use thinkModel;
use thinkmodelconcernSoftDelete;
class User extends Model
{
use SoftDelete;
protected $table = 'rbac_user';
protected $pk = 'id';
protected $deleteTime = 'delete_time';
// 自动时间戳
protected $autoWriteTimestamp = true;
protected $createTime = 'create_time';
protected $updateTime = 'update_time';
// 密码自动加密
public function setPasswordAttr($value)
{
return password_hash($value, PASSWORD_DEFAULT);
}
// 关联角色
public function roles()
{
return $this->belongsToMany('Role', 'rbac_user_role', 'role_id', 'user_id');
}
}
// app/admin/model/Role.php
class Role extends Model
{
protected $table = 'rbac_role';
protected $pk = 'id';
// 关联权限节点
public function nodes()
{
return $this->belongsToMany('Node', 'rbac_role_node', 'node_id', 'role_id');
}
// 关联用户
public function users()
{
return $this->belongsToMany('User', 'rbac_user_role', 'user_id', 'role_id');
}
}
// app/admin/model/Node.php
class Node extends Model
{
protected $table = 'rbac_node';
protected $pk = 'id';
// 获取树形结构
public static function getTree($pid = 0)
{
$nodes = self::where('pid', $pid)
->where('status', 1)
->order('sort', 'asc')
->select();
foreach ($nodes as &$node) {
$node['children'] = self::getTree($node['id']);
}
return $nodes;
}
}
?>
四、RBAC权限架构设计
权限验证服务类
<?php
// app/common/service/Permission.php
namespace appcommonservice;
use thinkfacadeSession;
use appadminmodelUser;
class Permission
{
// 检查权限
public static function check($node)
{
$user = Session::get('admin_user');
if (!$user) {
return false;
}
// 超级管理员拥有所有权限
if ($user['id'] == 1) {
return true;
}
$userModel = User::with(['roles.nodes'])->find($user['id']);
$userNodes = [];
foreach ($userModel->roles as $role) {
foreach ($role->nodes as $node) {
$userNodes[] = $node->path;
}
}
return in_array($node, array_unique($userNodes));
}
// 获取用户菜单
public static function getMenus()
{
$user = Session::get('admin_user');
if (!$user) {
return [];
}
$userModel = User::with(['roles.nodes'])->find($user['id']);
$menuNodes = [];
foreach ($userModel->roles as $role) {
foreach ($role->nodes as $node) {
if ($node->type == 1) { // 菜单类型
$menuNodes[] = $node->toArray();
}
}
}
return self::buildTree(array_unique($menuNodes, SORT_REGULAR));
}
// 构建菜单树
private static function buildTree($nodes, $pid = 0)
{
$tree = [];
foreach ($nodes as $node) {
if ($node['pid'] == $pid) {
$children = self::buildTree($nodes, $node['id']);
if ($children) {
$node['children'] = $children;
}
$tree[] = $node;
}
}
return $tree;
}
}
?>
五、中间件与权限验证
权限验证中间件
<?php
// app/admin/middleware/Auth.php
namespace appadminmiddleware;
use thinkfacadeSession;
use appcommonservicePermission;
class Auth
{
public function handle($request, Closure $next)
{
// 排除登录页面
$allowUrl = ['admin/auth/login', 'admin/auth/logout'];
$currentUrl = $request->controller() . '/' . $request->action();
if (!in_array($currentUrl, $allowUrl)) {
// 检查登录状态
if (!Session::has('admin_user')) {
return redirect('/admin/auth/login');
}
// 检查权限
$node = strtolower($request->controller() . '/' . $request->action());
if (!Permission::check($node)) {
if ($request->isAjax()) {
return json(['code' => 403, 'msg' => '没有访问权限']);
} else {
return view('admin@public/error', ['msg' => '没有访问权限']);
}
}
}
return $next($request);
}
}
中间件注册配置
// app/admin/middleware.php
return [
// 全局中间件
appadminmiddlewareAuth::class,
// 路由中间件别名
'alias' => [
'auth' => appadminmiddlewareAuth::class,
'log' => appadminmiddlewareOperationLog::class,
],
];
操作日志中间件
<?php
// app/admin/middleware/OperationLog.php
namespace appadminmiddleware;
use thinkfacadeSession;
use appadminmodelOperationLog as LogModel;
class OperationLog
{
public function handle($request, Closure $next)
{
$response = $next($request);
// 记录操作日志
if ($request->isPost() || $request->isDelete()) {
$user = Session::get('admin_user');
$logData = [
'user_id' => $user['id'] ?? 0,
'username' => $user['username'] ?? '',
'ip' => $request->ip(),
'user_agent' => $request->header('user-agent'),
'method' => $request->method(),
'path' => $request->pathinfo(),
'params' => json_encode($request->param(), JSON_UNESCAPED_UNICODE),
'create_time' => date('Y-m-d H:i:s')
];
LogModel::create($logData);
}
return $response;
}
}
六、后台管理模块实现
用户管理控制器
<?php
// app/admin/controller/User.php
namespace appadmincontroller;
use thinkfacadeRequest;
use appadminmodelUser as UserModel;
use appadminmodelRole as RoleModel;
class User extends Base
{
// 用户列表
public function index()
{
if (Request::isAjax()) {
$page = Request::param('page', 1);
$limit = Request::param('limit', 15);
$keywords = Request::param('keywords', '');
$where = [];
if (!empty($keywords)) {
$where[] = ['username|realname|email', 'like', "%{$keywords}%"];
}
$list = UserModel::with(['roles'])
->where($where)
->page($page, $limit)
->select();
$total = UserModel::where($where)->count();
return json([
'code' => 0,
'msg' => 'success',
'count' => $total,
'data' => $list
]);
}
return view();
}
// 添加用户
public function add()
{
if (Request::isPost()) {
$data = Request::param();
// 验证数据
$validate = new appadminvalidateUser();
if (!$validate->check($data)) {
return json(['code' => 1, 'msg' => $validate->getError()]);
}
// 创建用户
$user = UserModel::create($data);
if ($user) {
// 分配角色
if (!empty($data['role_ids'])) {
$user->roles()->saveAll($data['role_ids']);
}
return json(['code' => 0, 'msg' => '添加成功']);
} else {
return json(['code' => 1, 'msg' => '添加失败']);
}
}
// 获取角色列表
$roles = RoleModel::where('status', 1)->select();
$this->assign('roles', $roles);
return view();
}
// 编辑用户
public function edit($id)
{
$user = UserModel::with(['roles'])->find($id);
if (!$user) {
$this->error('用户不存在');
}
if (Request::isPost()) {
$data = Request::param();
// 如果密码为空,则不更新密码
if (empty($data['password'])) {
unset($data['password']);
}
if ($user->save($data)) {
// 更新角色
if (isset($data['role_ids'])) {
$user->roles()->detach();
$user->roles()->saveAll($data['role_ids']);
}
return json(['code' => 0, 'msg' => '更新成功']);
} else {
return json(['code' => 1, 'msg' => '更新失败']);
}
}
$roles = RoleModel::where('status', 1)->select();
$userRoles = array_column($user->roles->toArray(), 'id');
$this->assign([
'user' => $user,
'roles' => $roles,
'userRoles' => $userRoles
]);
return view();
}
}
基础控制器类
<?php
// app/admin/controller/Base.php
namespace appadmincontroller;
use thinkController;
use thinkfacadeSession;
use appcommonservicePermission;
class Base extends Controller
{
protected function initialize()
{
parent::initialize();
// 获取当前用户信息
$user = Session::get('admin_user');
$this->assign('admin_user', $user);
// 获取菜单
$menus = Permission::getMenus();
$this->assign('menus', $menus);
// 当前控制器和方法
$this->assign('controller', $this->request->controller());
$this->assign('action', $this->request->action());
}
// 成功跳转
protected function success($msg = '操作成功', $url = null)
{
if (is_null($url)) {
$url = $this->request->isAjax() ? '' : 'javascript:history.back()';
}
return json(['code' => 0, 'msg' => $msg, 'url' => $url]);
}
// 错误跳转
protected function error($msg = '操作失败', $url = null)
{
if (is_null($url)) {
$url = $this->request->isAjax() ? '' : 'javascript:history.back()';
}
return json(['code' => 1, 'msg' => $msg, 'url' => $url]);
}
}
七、前后端分离实践
API应用配置
// app/api/controller/Base.php
namespace appapicontroller;
use thinkController;
use thinkfacadeRequest;
class Base extends Controller
{
protected $userInfo = [];
protected function initialize()
{
// 跨域设置
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Headers: Authorization, Content-Type, Token');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
if (Request::isOptions()) {
exit();
}
// JWT token验证
$token = Request::header('Authorization');
if ($token) {
$this->userInfo = $this->verifyToken($token);
}
}
// JWT token验证
private function verifyToken($token)
{
try {
$payload = JWT::decode($token, config('jwt.secret'), ['HS256']);
return (array)$payload;
} catch (Exception $e) {
return [];
}
}
// 统一响应格式
protected function jsonSuccess($data = [], $msg = 'success')
{
return json([
'code' => 200,
'msg' => $msg,
'data' => $data
]);
}
protected function jsonError($msg = 'error', $code = 400)
{
return json([
'code' => $code,
'msg' => $msg
]);
}
}
八、部署与性能优化
Docker部署配置
# docker-compose.yml
version: '3.8'
services:
nginx:
image: nginx:1.21
ports:
- "80:80"
volumes:
- ./:/var/www/html
- ./docker/nginx.conf:/etc/nginx/nginx.conf
depends_on:
- php
php:
image: php:8.0-fpm
volumes:
- ./:/var/www/html
environment:
- PHP_EXTENSION=pdo_mysql,redis,gd
mysql:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=123456
- MYSQL_DATABASE=rbac_system
volumes:
- mysql_data:/var/lib/mysql
redis:
image: redis:6.2
ports:
- "6379:6379"
volumes:
mysql_data:
性能优化配置
// config/cache.php - Redis缓存配置
return [
'default' => 'redis',
'stores' => [
'redis' => [
'type' => 'redis',
'host' => '127.0.0.1',
'port' => 6379,
'password' => '',
'select' => 0,
'timeout' => 0,
'persistent' => false,
'prefix' => 'rbac:',
],
],
];
// 路由缓存
php think optimize:route
// 配置缓存
php think optimize:config
// 自动加载缓存
php think optimize:schema
项目总结
实现的核心功能
- 完整的RBAC权限管理系统
- 多应用架构支持
- 前后端分离API接口
- 操作日志记录系统
- Docker容器化部署
技术亮点
- 基于ThinkPHP 6.x的现代化架构
- 灵活的权限节点管理
- 中间件化的权限验证
- 高性能的缓存优化
- 完善的错误处理机制
扩展建议
- 集成工作流引擎
- 实现数据权限控制
- 添加消息推送功能
- 集成第三方登录
- 实现分布式部署