博客列表 >基于ThinkPHP6.0的一个通用后台管理系统

基于ThinkPHP6.0的一个通用后台管理系统

祥子弟弟
祥子弟弟原创
2021年03月25日 15:38:132632浏览

RBAC(角色访问控制)设计模型的后台管理系统

RBAC 是一套成熟的权限模型,元素包括:用户、角色、权限。在 RBAC 模型中,权限与角色相关联,用户通过成为对应角色的成员,从而得到这些角色的权限。即用户关联角色,角色关联权限,可实现系统权限的灵活配置。在本篇文章介绍到的后台管理系统中,角色-用户是一对多的关系。

在本项目中前端框架使用的是 adminLTE,可以在本项目目录文件的 public 文件夹下找到。
数据库是 MySQL,一共有 6 张数据表(本来是 4 张的,后边加了个文章管理,多了个文章和文章分类表)

项目模块

1. 登录/退出模块

主要实现了登录和退出功能还有非法访问的功能。
由于是一个后台管理系统,所以没有涉及注册模块。作为一个登陆页面,而且是一个后台管理的登录页面,那么就不必那么的花哨,尽量的简洁美观就行,你可以给它起一个好听的名字,“xxx 后台管理”之类的。

接下来就是它需要实现的功能了,登录页面的逻辑是很简单的:

前端要做的就是给用户一个登录登录框,其中有用户名和密码以及验证码(注意:这里只是我想要的是让用户是用户名密码登录,你可以去设计其他的,可以是邮箱呀之类的等等),在前端的验证码这块儿使用的是 ThinkPHP 的一个扩展框架,topthink/think-captcha ,你可以直接在你的项目目录下使用 composer 命令直接去将它加载进来。在密码和用户名还有验证码验证这块儿是用的jquery-validation,后边涉及到的表单验证都是使用的它,对于验证消息,我使用了 layui 的弹出层组件layer,后边的所有的提示消息,警告消息都是使用的layer
验证码这块儿是需要注意的,如果你想要去重新定义或者说是设置你的验证码,你可以在 config 目录下的 captcha.php 中去添加一个额外的验证码信息,然后在 Login 控制器下重新定义一个公共方法,让它去完成重新生成验证码的功能。如果你像我一样重新定义了验证码的话,那么你就会发现重新刷新页面后,你的验证码点击之后是无法进行刷新的,惊不惊喜,意不意外。这时候,你先不要着急,可以先不去用自定义的验证码,回到原来的验证码,看看点击之后页面之中会有什么样的变化,你会发现它的查询字符串是会变化的,它后边多了个 id,这时候是不是就醒悟了呢,你可以给这张验证码图片添加一个点击事件,点击之后重新的设置它的 src 属性的值就行。这样验证码的问题就解决了。

后端要做的就是将用户输入的信息获取到,然后再数据库中查找信息,找到之后去判断用户名和密码是否匹配,如果不匹配,就需要返回一个错误信息了,这个错误信息最好是一个模糊信息,最好就是”用户名或密码不正确” 而不是指出哪一个不正确。如果匹配的结果是成功的,那么你就需要将当前的到的用户名和数据表中查询到的用户 id 放到 Session 中了,tp6 中默认是关着的,你需要在全局中间件定义文件 middleware.php 中将它开启,开启之后你就可以使用 Session 了,因为在非法访问功能中会用到当前登录的用户信息。

我们还需要一个中间控制器,这个中间控制器继承自 BaseController,这个中间控制器的作用就是非法访问(没有登陆成功的不允许访问,直接访问非登录页面强制跳转到登陆页面)登录之后的权限认证(如果当前用户不具有这个功能的权限,会直接给个弹窗,然后返回到上一个页面),在登录之后才能访问的页面都要继承自这个中间控制器,这样就做到了非法访问的功能。

2.主页面模块

主页面的前端页面我用的是 adminLTE 中的 index.html 来修改的,它需要做到的效果就是能够根据不同的用户登录,然后自动去渲染菜单栏,还有用户的退出登录和清除缓存的功能。

自动渲染无限级菜单:这个就需要去数据库中获取对应角色的权限了,然后再根据角色对用户进行权限绑定,然后还需要一个无限级菜单的划分,这样对于数据的处理就可以传到前端进行渲染了。最难的部分应该就是无限级菜单的划分了,这个就得根据数据表中的数据是怎样的来设计处理函数了,我的菜单数据表中的有一个 pid 字段,它代表着当前菜单的上级菜单的 id 值,就可以对当前用户所具有的权限来进行查询,获取到他的权限字段的数据,然后再对这些权限根据菜单表中的 pid 字段进行处理,就可以得到一个想要的数据了。

  1. // 无限级分类--循环一级导航和子导航,将结果组合成一个多维数组
  2. public static function ruleLayer($rule, $pid=0): array
  3. {
  4. // 声明一个空数组,用来存放生成的多维数组
  5. $rules = array();
  6. foreach ($rule as $vl){
  7. // 判断当前的菜单是否是主菜单
  8. if ($vl['pid'] == $pid){
  9. // 如果是主菜单,就给它添加一个child字段,然后将所有pid===它的id的所有菜单赋给这个child字段
  10. $vl['child'] = static::ruleLayer($rule, $vl['id']);
  11. $rules[] = $vl;
  12. }
  13. }
  14. return $rules;
  15. }

前端渲染就可以使用 tp6 自己封装的 volist,然后进行二级菜单的渲染。

退出登录:这是一个很简单的逻辑实现,点击退出登录后,在后端将 session 中的数据清空,然后再将页面重新定向到登陆页面就可以了,可以使用 redirect()函数进行操作。

清除缓存:清除缓存,在 tp6 中就是清除掉 runtime 目录下的当前应用所产生的日志文件,还有一些缓存数据,这个操作就有些麻烦了,因为 rmdir()函数它只能删除一个空的文件夹,也就是说,想要删除一个文件夹,你得将这个文件夹下的所有内容全部的删掉,首先要获取到当前的应用的运行目录,可以直接使用 tp6 封装的助手函数来获取,app()->getRuntimePath();,然后再对当前文件夹下的内容进行删除,具体函数实现如下.

  1. function delete_dir_file($dir)
  2. {
  3. // 声明一个初始状态,默认情况下缓存未被删除
  4. $res = false;
  5. // 检验一个目录是否真实
  6. if (is_dir($dir)){
  7. // 成功打开目录流,返回值是一个resource类型数据,如果不成功,返回false
  8. if ($handle = opendir($dir)){
  9. while (($file = readdir($handle)) != false){
  10. // echo "filename: " . $file . "<br>";
  11. /*
  12. * filename: . 代表当前访问目录存在同级目录
  13. * filename: .. 代表存在上级目录
  14. * filename: log 子目录
  15. * filename: session 子目录
  16. * filename: temp 子目录
  17. */
  18. if ($file !== '.' && $file !== '..'){
  19. // 判断是否是一个目录
  20. if (is_dir($dir . '\\' . $file)){
  21. // 拼接目录
  22. // 目录只有为空的情况下才能删除
  23. delete_dir_file($dir . '\\' . $file);
  24. }else{
  25. // 不是目录的情况,直接删除
  26. // unlink只能删除一个文件
  27. unlink($dir . '\\' . $file);
  28. }
  29. }
  30. }
  31. }
  32. // 关闭目录句柄
  33. closedir($handle);
  34. // 目录为空时删除目录
  35. if ($dir !== "E:\\Visual Studio Code\\harbor\\tp\\runtime\\admin\\session\\"){
  36. if (rmdir($dir)){
  37. $res = true;
  38. }
  39. }
  40. }
  41. return $res;
  42. }

在主页面还可以将当前登录的用户信息也渲染出来,获取到当前 session 中的值,然后渲染到页面中.

3.用户管理模块

用户管理模块的数据显示这里我使用了 DataTables 表格插件,它是一款 jquery 表格插件,可以在DataTables 中文网查看它的使用教程。
用户管理模块主要是实现了用户的新增,编辑,删除功能,对于角色是超级管理员的用户,设置了不允许修改自己的权限还有不允许创建一个新的超级管理员。

对于用户管理模块首页的数据渲染,由于只涉及到了一张表,所以只需要注意 DataTables 插件要求返回的数据就行了,然后从数据库中取出查到的数据,返回给前端页面来进行渲染,如果在 DataTables 中设置了分页和排序要求,那么在设计数据库查询语句的时候就必须要添加上。

用户新增:用户新增涉及到两张数据表,一张是用户表,一张是用户-角色关联表,首先在前端页面的显示时,需要将角色表中的角色信息全部都获取出来,然后将获取到的角色信息传给前端进行一个下拉列表的渲染,然后就是前端进行添加一个新的用户操作,然后将数据 post 给后端,后端接收到数据之后,需要对用户名进行查重判断,如果不重复,先更新用户表,再更新用户-角色关联表,用户-角色关联表是在用户表更新成功的前提下进行更新的。如果重复,直接退出脚本,返回给前端一个信息,重定位到用户名那里。

用户编辑:用户编辑同样的涉及到了两张表,用户表和用户-角色关联表,编辑的时候需要获取到当前编辑的用户信息,然后渲染到页面,所以,可以通过 get 方法,将当前编辑的用户的 uid 传给后端,然后后端在数据库中查找记录,然后返回给前端进行渲染。更新的时候可新增一样,需要先对用户表进行更新,如果更新成功,再去对用户-角色关联表进行更新。

用户删除:也涉及到两张表,用户表和用户-角色关联表,再点击删除后,需要给用户一个提示信息,让他确定是否删除,然后再对删除方法进行调用,删除之后,给用户一个删除成功提示。

4.规则管理模块

规则管理是对菜单页面的显示,新增一级菜单,新增子菜单,编辑菜单还有删除菜单功能的实现。规则管理同样的使用了 DataTables 表格插件,不过对于上级菜单和子菜单如果需要进行效果展示的话,就需要对菜单进行无限级菜单处理,经过无限级菜单分类之后,就可以将数据传到前端进行渲染了。

  1. // 无限级菜单分类, 前端规则列表分类
  2. public static function RuleList($rule, $pid=0, $lev=1): array
  3. {
  4. $arr = array();
  5. foreach ($rule as $r){
  6. if ($r['pid'] == $pid){
  7. $r['lev'] = $lev;
  8. $arr[] = $r;
  9. $arr = array_merge($arr, static::RuleList($rule, $r['id'], $lev+1));
  10. }
  11. }
  12. return $arr;
  13. }

新增菜单:新增菜单这块儿有两个选择,一个是新增一个一级菜单,一个是新增一个子菜单,新增子菜单的时候是点击每个菜单规则后边的按钮,新增一级子菜单的时候是点击最上边的新增按钮,两个按钮唯一不同的地方是在新增子菜单的地方,get 时候的参数不同,子菜单的 url 的查询语句中增加了 pid,然后就可以在后端接收的时候进行检查,如果 get 到 pid 那就是新增一个子菜单,如果没有那就是新增一个一级菜单。

编辑菜单:编辑菜单需要获取到当前菜单的 id,然后在数据库中查找到当前菜单的信息,返回前端进行渲染,然后用户修改当前规则信息,返回数据给后端,后端将修改后的信息更新到数据库中去。

删除菜单:删除菜单和删除角色的功能类似,不过是删除菜单只涉及到一张数据表,只需要对一张数据表中的数据进行删除就可以了,删除菜单首先要做的就是查询当前菜单下有没有子菜单,将 id 当作 pid 传入数据表中进行查询,如果存在,就不能进行删除。

5.角色管理模块

角色管理模块中最难的就是一个 checkbox 复选框的处理,角色模块包括了角色的删除,新增和编辑,删除角色和上边的思路是一样的,而编辑和添加就不一样了,编辑和添加需要做到的是,首先在前端页面中需要将所有的规则列举出来,要有层级感,在编辑的时候需要对当前角色所拥有的权限全部都选中。同样,在后端获取到所有的菜单信息时,要对数据进行无限级菜单的处理,然后再传入前端进行渲染。

6.数据库模块

本项目目前一共涉及到 6 张表,其中有 4 张表是现在用到的,另外两张表是我添加的扩展功能所设计的数据表,现在用到的表有: 1. 用户表“users”,2. 角色表“auth_role”,3. 规则表“auth_rule”, 4. 角色-用户关联表“users_role”.

本项目的所有源码都可在 Git 上查看。

点击这里查看源码

声明:本文内容转载自脚本之家,由网友自发贡献,版权归原作者所有,如您发现涉嫌抄袭侵权,请联系admin@php.cn 核实处理。
全部评论
文明上网理性发言,请遵守新闻评论服务协议