企业🤖AI Agent构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
# 前言 ***** ### 对于后台管理系统,权限管理是最基本的一共功能, 一般会涉及3个概念 用户 角色 权限,今天将和大家分享一个最基本的权限设计, 涉及到4个表 + **用户表**<code>user</code> + **角色表** <code>role</code> + **权限表** <code>rule</code> + **角色-权限表** <code>role_rule </code> ![](https://box.kancloud.cn/fd01e5fbc36e0e7bcb39414f8d003935_899x585.jpg) > 因角色标识直接记录在<code>user</code> 表中的<code>role_guid</code> 字段,所有没有设计<code>user_role</code>表. ## 数据库设计 ### 用户表<code>user</code> | 字段 | 类型 | 描述 | --- | --- |---| | guid| char(36)|用户唯一标识 主键| | parent_guid| char(36)| 父级菜单唯一标识| | account| varchar(100)| 登陆账号| | name| varchar(100)| 用户姓名| | password| varchar(100)| 登陆密码| | role_guid| char(36)| 角色唯一标识| | status| tinyint(2)| 状态| | create_time| datetime(3)| 创建时间| | update_time| datetime(3)| 更新时间| | delete_time| datetime(3)| 删除时间| ``` DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `guid` char(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '主键', `account` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '工号', `name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户姓名', `password` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户密码', `role_guid` char(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '角色唯一标识', `status` tinyint(2) NOT NULL DEFAULT 0 COMMENT '是否启用', `tel` char(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '电话号码', `create_time` datetime(3) NOT NULL COMMENT '创建时间', `update_time` datetime(3) NOT NULL COMMENT '更新时间', `delete_time` datetime(3) NULL DEFAULT NULL COMMENT '删除时间', PRIMARY KEY (`guid`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Compact; SET FOREIGN_KEY_CHECKS = 1; ``` ### 角色表<code>role</code> | 字段 | 类型 | 描述 | --- | --- |---| | guid| char(36)|角色唯一标识 主键| | type| tinyint(2)| 登陆账号| | name| varchar(100)| 用户姓名| | status| tinyint(2)| 状态| | create_time| datetime(3)| 创建时间| | update_time| datetime(3)| 更新时间| | delete_time| datetime(3)| 删除时间| ``` DROP TABLE IF EXISTS `role`; CREATE TABLE `role` ( `guid` char(36) NOT NULL, `type` tinyint(2) NOT NULL DEFAULT '2' COMMENT '1管理员,2操作员', `name` varchar(32) NOT NULL COMMENT '角色名称', `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否启用', `remark` varchar(255) DEFAULT '' COMMENT '简单说明', `delete_time` datetime DEFAULT NULL COMMENT '删除时间', `create_time` datetime(3) DEFAULT NULL COMMENT '创建时间', `update_time` datetime(3) DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (`guid`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT COMMENT='角色表'; ``` ### 权限表<code>rule</code> | 字段 | 类型 | 描述 | --- | --- |---| | guid| char(36)|角色唯一标识 主键| | parent_guid| char(36) | 父级菜单唯一标识| | name| varchar(100)| url地址| | title| varchar(100)| 菜单名称| | icon| varchar(100)| 图标| | is_auth| tinyint(2)| 是否鉴权 | | is_link| tinyint(2)| 是否鉴权 1是0否| | status| tinyint(2)| 状态| | sort| int(4)| 排序| | create_time| datetime(3)| 创建时间| | update_time| datetime(3)| 更新时间| ``` DROP TABLE IF EXISTS `rule`; CREATE TABLE `rule` ( `guid` char(36) NOT NULL, `parent_guid` char(36) DEFAULT NULL COMMENT '父级菜单唯一标识', `name` varchar(100) NOT NULL COMMENT 'url地址', `title` varchar(100) NOT NULL COMMENT '菜单名称', `icon` varchar(100) DEFAULT NULL COMMENT '图标', `is_auth` tinyint(2) NOT NULL DEFAULT '1' COMMENT '是否鉴权 1是0否', `is_link` tinyint(2) NOT NULL DEFAULT '0' COMMENT '是否菜单 1是0否(比如api接口路径)', `sort` int(4) NOT NULL DEFAULT '255' COMMENT '排序', `status` tinyint(1) DEFAULT '1' COMMENT '状态1可用,0不可用', `update_time` datetime(3) NOT NULL COMMENT '更新时间', `create_time` datetime(3) NOT NULL COMMENT '创建时间', PRIMARY KEY (`guid`) USING BTREE, UNIQUE KEY `rulename` (`name`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT COMMENT='权限&菜单表'; ``` ### 角色&权限表<code>role_rule</code> | 字段 | 类型 | 描述 | --- | --- |---| | role_guid| char(36)|角色唯一标识| | rule_guid| char(36)|权限唯一标识| ``` DROP TABLE IF EXISTS `role_rule`; CREATE TABLE `role_rule` ( `role_guid` char(36) NOT NULL COMMENT '角色唯一标识', `rule_guid` char(36) NOT NULL COMMENT '权限唯一标识', UNIQUE KEY `uk_role_guid_rule_guid` (`role_guid`,`rule_guid`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT COMMENT='角色权限关联表'; ``` ## 本文重点讲述如何利用缓存来判断用户权限 >[warning] 请注意Thinkphp5.1.6+之后的版本才支持中间件。 官方中间件文档:阅读 https://www.kancloud.cn/manual/thinkphp5_1/564279 我们使用 thinkphp5的中间件特性来做统一验证, 首先我们在<code> application/test/middleware</code> 目录中创建 中间件文件 <code>Auth.php</code>文件 ```<?php namespace app\test\middleware; use app\common\model\Rule; use think\Controller; class Auth extends Controller { /** * 默认返回资源类型 * @var \think\Request $request * @var mixed $next * @var string $name * @throws \Exception * @return mixed */ public function handle($request, \Closure $next, $name) { $path = strtolower($request->controller() . '/' . $request->action()); $db_rule = new Rule(); $role_guid = ''; //todo 实际应用中 可以通过请求中携带access_token 利用JWT验证后 将得到的用户信息(user_guid/role_guid 等等 传入到model中获取 或者用session传递 if ($db_rule->checkRule($path, $role_guid) === false) { return json(['code' => -1, 'message' => '抱歉您没有权限!']); } return $next($request); } } ``` 然后在<code>application\test</code>目录下 新建<code>middleware.php</code>文件. ```<?php // 中间件扩展定义文件 return [ 'auth' => app\test\middleware\Auth::class ]; ``` ##模型<code>Rule</code>中部分代码参考 ``` <?php namespace app\common\model; use think\Db; use think\Model; class Rule extends Model { protected $pk = 'guid'; protected $exp = 3600 * 24; protected $type = [ 'is_link' => 'integer', 'is_auth' => 'integer', 'sort' => 'integer' ]; /** * 验证角色是否有权限 * @param string $path 当前路径 * @param string $role_guid 角色标识,没有传role_guid 获取登陆用户的用户组 * @throws \Exception * @return bool */ public function checkRule($path = '', $role_guid = null) { // 没有传path地址获取当前 if ($path == '') { $request = request(); $path = strtolower($request->controller() . '/' . $request->action()); } //只要当前请求路径是白名单中(rule表中is_auth=0)或者该用户已经有权限 则校验通过 return in_array($path, $this->getNoAuthRule()) || in_array($path, $this->getPermissionByRoleGuid($role_guid)); } /** * 获取白名单列表 * @throws \Exception * @return array */ public function getNoAuthRule() { $db = new Rule(); //使用cache写法如果存在缓存则不会查询数据库,非常方便 return $db->cache('rule:no_auth_list', $this->exp)->where('is_auth', 0)->column('name'); } /** * 根据角色guid获取权限列表 * @param string|null $role_guid 角色标识 * @throws \Exception * @return array */ public function getPermissionByRoleGuid($role_guid) { $cache_key = 'rule:role:' . $role_guid; $map = [ ['r.status', '=', 1], ['r.is_auth', '=', 1], ['rr.role_guid', '=', $role_guid] ]; $join = [['__RULE__ r', 'rr.rule_guid=r.guid']]; //使用cache写法如果存在缓存则不会查询数据库,非常方便 return Db::name('role_rule')->alias('rr')->join($join)->where($map)->cache($cache_key, $this->exp)->column('r.name'); } } ``` ## 开发帮助及交流 如您对本文感兴趣想与我联系交流 您可以 + 邮件至:xieyongfa@ecarde.cn + QQ:2392523899 [点我聊天](http://wpa.qq.com/msgrd?v=3&uin=2392523899&site=qq&menu=yes&from=message&isappinstalled=0) + 微信交流 ![](https://box.kancloud.cn/b74285a950ce81e3cb782f02eb118d59_752x974.jpg =300x389)