# 前言
*****
### 对于后台管理系统,权限管理是最基本的一共功能, 一般会涉及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)