## 权限管理主要由三部分组成:
1. 角色
2. 权限节点
3. 用户
### 对应关系如下:
**权限节点绑定到角色上面,角色再与用户绑定。**
*****
### **权限的验证**
#### 操作菜单如何展示?
1. 首先用户登陆
2. 根据用户id查到他的角色
3. 再根据角色查到他可用的操作节点
4. 然后对于操作节点自身的分组id进行分组
5. 最后将这些分组通过json传递到前端或者后端进行拼接后展示给用户。
6. 这样就实现了,如果用户没有操作权限的话就不会展示用户操作菜单。
*****
### 操作数据的验证如何进行?
**每一次用户点击操作菜单的时候,对于整个操作详情界面往往都有增删改查4种操作。
对于每一种操作,都按照上面的角色用户对应关系进行查询。**
1. 拿到用户的所的权限节点
2. 对于数据操作,首先判断用户权限节点中是否存在这些操作。
3. 有,则进行操作。
4. 没有,则提示用户没有权限。
### **在thinkphp5中如何实现权限的验证呢**?**
我用到了中间件来验证用户登陆情况,然后在后台模块的父控制器中,我根据用户的登陆情况对用户的可操作节点进行了判断。
中间件代码如下,更多的请看代码库中的代码:[https://gitee.com/leihenshang/gossip_blog](https://gitee.com/leihenshang/gossip_blog)
```
<?php
namespace app\\http\\middleware;
use app\\common\\controller\\BackendBase;
class Check
{
public function handle($request, \\Closure $next)
{
$backendBaseObject = new BackendBase();
$backendBaseObject\->checkLoginStatus();
return $next($request);
}
}
```
权限验证代码:
```
<?php
namespace app\\common\\controller;
use app\\common\\model\\Permission;
use app\\common\\model\\RolePermissionRelation;
use app\\common\\model\\UserRoleRelation;
use app\\common\\model\\User;
use think\\Controller;
use think\\facade\\Cookie;
use think\\facade\\Request;
use think\\facade\\Session;
class BackendBase extends Controller
{
//超级管理员名称
protected $superUserName = 'tangq';
//指定中间件
protected $middleware = \['Check'\];
//存储用户信息和用户权限节点在当前类的子类中共享
protected $userinfo = null;
public static $userPermission = null;
//设置是否进行权限验证,true验证,false不验证
public function checkPermission()
{
return false;
}
/\*\*
\* 获取用户信息
\* @param int $type 1为session 2 为Cookie
\* @return void
\*/
public function getUserInfo($type = 1)
{
if ($type === 1) {
$userinfo = Session::get('userinfo');
if (empty($userinfo) || $userinfo\['expire\_time'\] < time()) {
$userinfo = false;
}
return $userinfo;
} else {
$userinfo = Cookie::get('userinfo');
$userinfo = json\_decode($userinfo, true);
if (empty($userinfo) || $userinfo\['expire\_time'\] < time()) {
$userinfo = false;
}
return $userinfo;
}
}
/\*\*
\* 检查用户登陆状态
\*/
public function checkLoginStatus()
{
//获取用户登陆信息
$this\->userinfo = $this\->getUserInfo();
if($this\->userinfo === false){
// return returnData(false,'获取用户信息失败');
$this\->error('重新登陆','index/user/index','',1);
}
//检查用户是否在其他地方登录
if (Request::controller() == 'MyCenter') {
if (false === User::checkOnlyLogin($this\->userinfo)) {
User::clearUserinfo();
$this\->error('您可能在其他地方登录,请重新登录尝试', 'index/user/index', '', 1);
}
}
if (true === $this\->checkPermission()) {
//后台页面权限控制
if (Request::controller() == 'MyCenter') {
if (!$this\->userinfo) {
$this\->error('用户信息错误,请重新登陆', 'index/user/index', '', 1);
}
//根据用户信息获取当前用户全部可访问节点
$userPermission = $this\->getPermissionNode($this\->userinfo);
//获取请求路径信息
$pathInfo = $this\->getUserRequestPath($userPermission);
//判断权限验证结果
if ($pathInfo === false) {
$this\->error('没有访问权限', 'index/index/index', '', 1);
}
} else {
//定义json返回结构
$data = \['success' => false, 'msg' => '没有访问权限', 'data' => \[\], 'status' => 200\];
//没有找到用户信息的情况
if (!$this\->userinfo) {
//指定json header头
header('Content-type:text/json');
echo json\_encode($data);
die;
}
//判断节点权限
$userPermission = $this\->getPermissionNode($this\->userinfo);
//获取请求路径信息
$pathInfo = $this\->getUserRequestPath($userPermission);
//判断权限验证结果
if ($pathInfo === false) {
//指定json header头
header('Content-type:text/json');
echo json\_encode($data);
die;
}
}
} else {
$this\->userinfo\->username = 'tangq';
$userPermission = $this\->getPermissionNode($this\->userinfo);
}
//存储用户可访问节点
self::$userPermission = $userPermission;
// $this->userPermission = $userPermission;
//为模板写入用户信息
$this\->assign('userinfo', $this\->userinfo);
//为模板写入后台管理菜单列表
$this\->assign('menuList', $this\->getMenu($this\->userinfo));
}
/\*\*
\* 获取用户真实请求路径,返回用户请求的路径的数组
\* @param array $userPermission 用户节点信息一维数组
\* @return void
\*/
public function getUserRequestPath($userPermission = \[\])
{
if (empty($userPermission)) {
return false;
}
$pathInfo = \[Request::path()\];
$routeInfo = Request::routeInfo();
if (!empty($routeInfo)) {
$pathInfo\[\] = $routeInfo\['route'\];
}
//判断节点权限
$res = false;
foreach ($pathInfo as $pathValue) {
if (in\_array($pathValue, $userPermission)) {
$res = true;
break;
}
}
//返回结果
return $res;
}
/\*\*
\* 根据用户信息获取权限节点
\*
\* @param object $userId
\* @param int $nodeType 节点类型 0 全部,1分组,2页面,3功能性节点
\* @return void
\*/
public function getPermissionNode($userInfo, $nodeType = 0)
{
if (!$userInfo) {
return \[\];
}
//超级用户返回所有权限节点
if ($userInfo\->username === $this\->superUserName) {
$userPermissionUrl = Permission::column('node\_url');
return $userPermissionUrl;
}
//根据用户id获取用户属于哪个角色
$userRoleId = UserRoleRelation::where('user\_id', $userInfo\->id)->column('role\_id');
if (!$userRoleId) {
return \[\];
}
//获取用户所属角色的权限节点id
$userPermissionId = RolePermissionRelation::where('role\_id', 'in', $userRoleId)->where('is\_deleted', 0)->column('permission\_id');
//获取权限节点url
$userPermissionUrl = Permission::where('id', 'in', $userPermissionId)->where('is\_deleted', 0)->column('node\_url');
if ($nodeType != 0) {
$userPermissionUrl\->where('type', $nodeType);
}
return $userPermissionUrl;
}
/\*\*
\* 获取后台管理菜单列表
\*
\* @param string $userinfo 用户信息
\*
\* @return array
\*/
public function getMenu($userinfo)
{
$arr = \[\];
if (!empty($userinfo)) {
if ($userinfo\->username == $this\->superUserName) {
//获取用户用户的菜单列表
$resMenu = Permission::field('id,name,node\_url,p\_id,is\_expand')->where('type', 'in', \[0, 1\])->select()->toArray();
} else {
//获取用户属于哪个角色
$userRole = UserRoleRelation::where('user\_id', $userinfo\->id)->column('role\_id');
if (!$userRole) {
$this\->error('没有指定用户的角色', '', '', 1);
}
//获取用户所属角色的权限节点
$userPermission = RolePermissionRelation::where('role\_id', 'in', $userRole)->where('is\_deleted', 0)->column('permission\_id');
//获取权限节点
$resMenu = Permission::field('id,name,node\_url,p\_id,is\_expand')->where('type', 'in', \[0, 1\])->where('is\_deleted', 0)->where('id', 'in', $userPermission)->select()->toArray();
}
} else {
return \[\];
}
//处理url以及设置子节点
foreach ($resMenu as $value) {
$value\['node\_url'\] = $value\['node\_url'\] == '#' ? 'javascript:;' : $value\['node\_url'\]; //处理url
if ($value\['node\_url'\] != 'javascript:;') {
$value\['node\_url'\] = mb\_substr($value\['node\_url'\], 0, 1) == '/' ? $value\['node\_url'\] : '/' . $value\['node\_url'\];
}
$value\['child'\] = \[\];
$arr\[$value\['id'\]\] = $value;
}
//生成父子节点
foreach ($resMenu as $key => $value) {
if (isset($arr\[$value\['p\_id'\]\])) {
$arr\[$value\['p\_id'\]\]\['child'\]\[\] = $value;
unset($arr\[$value\['id'\]\]);
}
}
return $arr;
}
}
```