# 前言
*****
### 對于后臺管理系統,權限管理是最基本的一共功能, 一般會涉及3個概念 用戶 角色 權限,今天將和大家分享一個最基本的權限設計, 涉及到4個表
+ **用戶表**<code>user</code>
+ **角色表** <code>role</code>
+ **權限表** <code>rule</code>
+ **角色-權限表** <code>role_rule </code>

> 因角色標識直接記錄在<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+之后的版本才支持中間件。
官方中間件文檔:閱讀 http://www.hmoore.net/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)
+ 微信交流
