├── .gitignore ├── src ├── model │ ├── UserRole.php │ ├── RolePermission.php │ ├── Base.php │ ├── PermissionCategory.php │ ├── Permission.php │ └── Role.php ├── validate │ ├── Role.php │ ├── PermissionCategory.php │ └── Permission.php ├── CreateTable.php └── Rbac.php ├── composer.json ├── LICENSE ├── gmars_rbac.sql └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /src/model/UserRole.php: -------------------------------------------------------------------------------- 1 | 5 | * Date: 2019-04-17 6 | * Time: 22:51 7 | */ 8 | 9 | namespace gmars\rbac\model; 10 | 11 | 12 | class UserRole extends Base 13 | { 14 | 15 | } -------------------------------------------------------------------------------- /src/model/RolePermission.php: -------------------------------------------------------------------------------- 1 | 5 | * Date: 2019-04-17 6 | * Time: 22:51 7 | */ 8 | 9 | namespace gmars\rbac\model; 10 | 11 | 12 | class RolePermission extends Base 13 | { 14 | 15 | } -------------------------------------------------------------------------------- /src/model/Base.php: -------------------------------------------------------------------------------- 1 | 5 | * Date: 2019-04-17 6 | * Time: 22:52 7 | */ 8 | 9 | namespace gmars\rbac\model; 10 | 11 | 12 | use think\Model; 13 | 14 | class Base extends Model 15 | { 16 | protected $connection = ''; 17 | 18 | public function __construct($db = '', $data = []) 19 | { 20 | parent::__construct($data); 21 | $this->connection = empty($db)? config('rbac')['db'] : $db; 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gmars/tp5-rbac", 3 | "description": "这个扩展是基于thinkPHP5框架的RBAC权限验证的扩展。使用本扩展能够快速的将RBAC权限控制器模块引入到自己的系统中。原则上本模块适合于任何PHP框架但是由于使用了thinkPHP5的特性所以使用composer安装到其他框架时需要做相应的修改。模块所需要的扩展库可以通过thinkPHP5的migration来做数据迁移。", 4 | "type": "library", 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "weiyongqiang", 9 | "email": "hayixia606@163.com" 10 | } 11 | ], 12 | "minimum-stability": "dev", 13 | "require": { 14 | }, 15 | "autoload": { 16 | "psr-4": { 17 | "gmars\\rbac\\": "src/" 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/validate/Role.php: -------------------------------------------------------------------------------- 1 | 5 | * Date: 2019-04-17 6 | * Time: 22:54 7 | */ 8 | 9 | namespace gmars\rbac\validate; 10 | 11 | 12 | use think\Validate; 13 | 14 | class Role extends Validate 15 | { 16 | protected $rule = [ 17 | 'name' => 'require|max:50|unique:gmars\rbac\model\role,name^id' 18 | ]; 19 | 20 | protected $message = [ 21 | 'name.require' => '角色名不能为空', 22 | 'name.max' => '角色名不能长于50个字符', 23 | 'name.unique' => '角色名称不能重复' 24 | ]; 25 | 26 | } -------------------------------------------------------------------------------- /src/validate/PermissionCategory.php: -------------------------------------------------------------------------------- 1 | 5 | * Date: 2019-04-17 6 | * Time: 22:54 7 | */ 8 | 9 | namespace gmars\rbac\validate; 10 | 11 | 12 | use think\Validate; 13 | 14 | class PermissionCategory extends Validate 15 | { 16 | protected $rule = [ 17 | 'name' => 'require|max:50|unique:gmars\rbac\model\permissioncategory,name', 18 | ]; 19 | 20 | protected $message = [ 21 | 'name.require' => '分组名不能为空', 22 | 'name.max' => '分组名不能长于50个字符', 23 | 'name.unique' => '分组名称不能重复', 24 | ]; 25 | 26 | } -------------------------------------------------------------------------------- /src/validate/Permission.php: -------------------------------------------------------------------------------- 1 | 5 | * Date: 2019-04-17 6 | * Time: 22:54 7 | */ 8 | 9 | namespace gmars\rbac\validate; 10 | 11 | 12 | use think\Validate; 13 | 14 | class Permission extends Validate 15 | { 16 | protected $rule = [ 17 | 'name' => 'require|max:50|unique:gmars\rbac\model\permission,name', 18 | 'path' => 'require|max:200|unique:gmars\rbac\model\permission,path', 19 | 'category_id' => 'require|number', 20 | 'type' => 'require' 21 | ]; 22 | 23 | protected $message = [ 24 | 'name.require' => '权限名不能为空', 25 | 'name.max' => '权限名不能长于50个字符', 26 | 'path.require' => '路径不能为空', 27 | 'path.max' => '路径不能长于200个字符', 28 | 'category_id.require' => '权限分类必须选择', 29 | 'category_id.number' => '权限分类必须是数字id', 30 | 'name.unique' => '权限名称不能重复', 31 | 'path.unique' => '权限路径不能为空', 32 | 'type.require' => '权限类型不能为空' 33 | ]; 34 | 35 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 weiyongqiang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/CreateTable.php: -------------------------------------------------------------------------------- 1 | _lockFile = Env::get('root_path') . 'runtime/rbac_sql.lock'; 23 | $this->_sqlFile = dirname(__DIR__) . '/gmars_rbac.sql'; 24 | } 25 | 26 | /** 27 | * 创建数据表 28 | * @param string $db 29 | */ 30 | public function create($db = '') 31 | { 32 | $dbConfig = Db::getConfig(); 33 | $prefix = $db == ''? $dbConfig['prefix'] : $dbConfig[$db]['prefix']; 34 | 35 | if (file_exists($this->_lockFile)) { 36 | echo "数据库创建操作被锁定,请删除[{$this->_lockFile}]文件后重试"; 37 | exit; 38 | } 39 | 40 | if ($this->_generateSql($prefix) === false) { 41 | echo '执行sql语句出错,请检查配置'; 42 | exit; 43 | } 44 | echo '执行成功,如非必要请不要解锁后再次执行,重复执行会清空原有rbac表中的数据'; 45 | $this->_writeLock(); 46 | exit; 47 | } 48 | 49 | /** 50 | * 执行sql语句 51 | * @param string $prefix 52 | * @return bool 53 | */ 54 | private function _generateSql($prefix = '') 55 | { 56 | $sql = $this->_loadSqlFile(); 57 | $prefix = empty($prefix)? '' : $prefix; 58 | $sql = str_replace('###', $prefix, $sql); 59 | $sqlArr = explode(';', $sql); 60 | if (Db::batchQuery($sqlArr) === false) { 61 | return false; 62 | } 63 | return true; 64 | } 65 | 66 | /** 67 | * 加载sql文件 68 | * @return bool|string 69 | */ 70 | private function _loadSqlFile() 71 | { 72 | $fileObj = fopen($this->_sqlFile, 'r'); 73 | $sql = fread($fileObj, filesize($this->_sqlFile)); 74 | fclose($fileObj); 75 | return $sql; 76 | } 77 | 78 | /** 79 | * 创建数据库操作锁 80 | */ 81 | private function _writeLock() 82 | { 83 | $fileObj = fopen($this->_lockFile, 'w'); 84 | fwrite($fileObj, date("Y-m-d H:i:s") . '执行成功!'); 85 | fclose($fileObj); 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/model/PermissionCategory.php: -------------------------------------------------------------------------------- 1 | 5 | * Date: 2019-04-17 6 | * Time: 22:50 7 | */ 8 | 9 | namespace gmars\rbac\model; 10 | 11 | 12 | use think\Db; 13 | use think\Exception; 14 | 15 | class PermissionCategory extends Base 16 | { 17 | 18 | /** 19 | * 编辑权限分组 20 | * @param $data 21 | * @return $this 22 | * @throws Exception 23 | */ 24 | public function saveCategory($data = []) 25 | { 26 | if (!empty($data)) { 27 | $this->data($data); 28 | } 29 | $validate = new \gmars\rbac\validate\PermissionCategory(); 30 | if (!$validate->check($this)) { 31 | throw new Exception($validate->getError()); 32 | } 33 | $data = $this->getData(); 34 | if (isset($data['id']) && !empty($data['id'])) { 35 | $this->isUpdate(true); 36 | } 37 | $this->save(); 38 | return $this; 39 | } 40 | 41 | /** 42 | * 删除权限分组 43 | * @param $id 44 | * @return bool 45 | * @throws Exception 46 | */ 47 | public function delCategory($id) 48 | { 49 | $where = []; 50 | if (is_array($id)) { 51 | $where[] = ['id', 'IN', $id]; 52 | } else { 53 | $id = (int)$id; 54 | if (is_numeric($id) && $id > 0) { 55 | $where[] = ['id', '=', $id]; 56 | } else { 57 | throw new Exception('删除条件错误'); 58 | } 59 | } 60 | 61 | if ($this->where($where)->delete() === false) { 62 | throw new Exception('删除权限分组出错'); 63 | } 64 | return true; 65 | } 66 | 67 | /** 68 | * 获取权限分组 69 | * @param $where 70 | * @return array|\PDOStatement|string|\think\Collection|\think\Model|null 71 | * @throws \think\db\exception\DataNotFoundException 72 | * @throws \think\db\exception\ModelNotFoundException 73 | * @throws \think\exception\DbException 74 | */ 75 | public function getCategory($where) 76 | { 77 | $model = Db::name('permission_category')->setConnection($this->getConnection()); 78 | if (is_numeric($where)) { 79 | return $model->where('id', $where)->find(); 80 | } else { 81 | return $model->where($where)->select(); 82 | } 83 | } 84 | 85 | } -------------------------------------------------------------------------------- /gmars_rbac.sql: -------------------------------------------------------------------------------- 1 | SET FOREIGN_KEY_CHECKS=0; 2 | DROP TABLE IF EXISTS `###permission_category`; 3 | CREATE TABLE `###permission_category` ( 4 | `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 5 | `name` varchar(50) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '权限分组名称', 6 | `description` varchar(200) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '权限分组描述', 7 | `status` smallint(4) unsigned NOT NULL DEFAULT '1' COMMENT '权限分组状态1有效2无效', 8 | `create_time` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '权限分组创建时间', 9 | PRIMARY KEY (`id`) 10 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; 11 | DROP TABLE IF EXISTS `###permission`; 12 | CREATE TABLE `###permission` ( 13 | `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 14 | `name` varchar(50) NOT NULL DEFAULT '' COMMENT '权限节点名称', 15 | `type` smallint(4) unsigned NOT NULL DEFAULT '0' COMMENT '权限类型1api权限2前路由权限', 16 | `category_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '权限分组id', 17 | `path` varchar(100) NOT NULL DEFAULT '' COMMENT '权限路径', 18 | `path_id` varchar(100) NOT NULL DEFAULT '' COMMENT '路径唯一编码', 19 | `description` varchar(200) NOT NULL DEFAULT '' COMMENT '描述信息', 20 | `status` smallint(4) unsigned NOT NULL DEFAULT '0' COMMENT '状态0未启用1正常', 21 | `create_time` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', 22 | PRIMARY KEY (`id`), 23 | KEY `idx_permission` (`path_id`,`status`) 24 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='权限节点'; 25 | DROP TABLE IF EXISTS `###role`; 26 | CREATE TABLE `###role` ( 27 | `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 28 | `name` varchar(50) NOT NULL DEFAULT '' COMMENT '角色名', 29 | `description` varchar(200) NOT NULL DEFAULT '' COMMENT '角色描述', 30 | `status` smallint(4) unsigned NOT NULL DEFAULT '0' COMMENT '状态1正常0未启用', 31 | `sort_num` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '排序值', 32 | PRIMARY KEY (`id`), 33 | KEY `idx_role` (`status`) 34 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色'; 35 | DROP TABLE IF EXISTS `###role_permission`; 36 | CREATE TABLE `###role_permission` ( 37 | `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 38 | `role_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '角色编号', 39 | `permission_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '权限编号', 40 | PRIMARY KEY (`id`) 41 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色权限对应表'; 42 | DROP TABLE IF EXISTS `###user`; 43 | CREATE TABLE `###user` ( 44 | `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 45 | `user_name` varchar(50) NOT NULL DEFAULT '' COMMENT '用户名', 46 | `password` varchar(64) NOT NULL DEFAULT '' COMMENT '用户密码', 47 | `mobile` varchar(20) NOT NULL DEFAULT '' COMMENT '手机号码', 48 | `last_login_time` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最后一次登录时间', 49 | `status` smallint(4) unsigned NOT NULL DEFAULT '0' COMMENT '状态0禁用1正常', 50 | `create_time` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '账号创建时间', 51 | `update_time` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '信息更新时间', 52 | PRIMARY KEY (`id`), 53 | KEY `idx_user` (`user_name`,`mobile`,`status`) 54 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表'; 55 | DROP TABLE IF EXISTS `###user_role`; 56 | CREATE TABLE `###user_role` ( 57 | `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 58 | `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', 59 | `role_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '角色id', 60 | PRIMARY KEY (`id`) 61 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户角色对应关系' -------------------------------------------------------------------------------- /src/model/Permission.php: -------------------------------------------------------------------------------- 1 | 5 | * Date: 2019-04-17 6 | * Time: 22:49 7 | */ 8 | 9 | namespace gmars\rbac\model; 10 | 11 | 12 | 13 | use think\Db; 14 | use think\Exception; 15 | use think\facade\Cache; 16 | use think\facade\Session; 17 | 18 | class Permission extends Base 19 | { 20 | /** 21 | * @var string 权限缓存前缀 22 | */ 23 | private $_permissionCachePrefix = "_RBAC_PERMISSION_CACHE_"; 24 | 25 | protected $auto = ['path_id']; 26 | 27 | protected function setPathIdAttr() 28 | { 29 | return md5($this->getData('path')); 30 | } 31 | 32 | /** 33 | * 编辑权限数据 34 | * @param array $data 35 | * @return $this 36 | * @throws Exception 37 | */ 38 | public function savePermission($data = []) 39 | { 40 | if (!empty($data)) { 41 | $this->data($data); 42 | } 43 | $validate = new \gmars\rbac\validate\Permission(); 44 | if (!$validate->check($this)) { 45 | throw new Exception($validate->getError()); 46 | } 47 | $data = $this->getData(); 48 | if (isset($data['id']) && !empty($data['id'])) { 49 | $this->isUpdate(true); 50 | } 51 | $this->save(); 52 | return $this; 53 | } 54 | 55 | /** 56 | * 删除权限 57 | * @param $id 58 | * @return bool 59 | * @throws Exception 60 | */ 61 | public function delPermission($id) 62 | { 63 | $where = []; 64 | if (is_array($id)) { 65 | $where[] = ['id', 'IN', $id]; 66 | } else { 67 | $id = (int)$id; 68 | if (is_numeric($id) && $id > 0) { 69 | $where[] = ['id', '=', $id]; 70 | } else { 71 | throw new Exception('删除条件错误'); 72 | } 73 | } 74 | 75 | if ($this->where($where)->delete() === false) { 76 | throw new Exception('删除权限出错'); 77 | } 78 | return true; 79 | } 80 | 81 | /** 82 | * 获取用户权限 83 | * @param $userId 84 | * @param int $timeOut 85 | * @return array|mixed|\PDOStatement|string|\think\Collection 86 | * @throws Exception 87 | * @throws \think\db\exception\DataNotFoundException 88 | * @throws \think\db\exception\ModelNotFoundException 89 | * @throws \think\exception\DbException 90 | */ 91 | public function userPermission($userId, $timeOut = 3600) 92 | { 93 | if (empty($userId)) { 94 | throw new Exception('参数错误'); 95 | } 96 | $permission = Cache::get($this->_permissionCachePrefix . $userId); 97 | if (!empty($permission)) { 98 | return $permission; 99 | } 100 | $permission = $this->getPermissionByUserId($userId); 101 | if (empty($permission)) { 102 | throw new Exception('未查询到该用户的任何权限'); 103 | } 104 | $newPermission = []; 105 | if (!empty($permission)) { 106 | foreach ($permission as $k=>$v) 107 | { 108 | $newPermission[$v['path']] = $v; 109 | } 110 | } 111 | Cache::set($this->_permissionCachePrefix . $userId, $newPermission, $timeOut); 112 | Session::set('gmars_rbac_permission_name', $this->_permissionCachePrefix . $userId); 113 | return $newPermission; 114 | } 115 | 116 | /** 117 | * 根据userid获取权限 118 | * @param $userId 119 | * @return array|\PDOStatement|string|\think\Collection 120 | * @throws \think\db\exception\DataNotFoundException 121 | * @throws \think\db\exception\ModelNotFoundException 122 | * @throws \think\exception\DbException 123 | */ 124 | public function getPermissionByUserId($userId) 125 | { 126 | $prefix = $this->getConfig('prefix'); 127 | $permission = Db::name('permission')->setConnection($this->getConnection())->alias('p') 128 | ->join(["{$prefix}role_permission" => 'rp'], 'p.id = rp.permission_id') 129 | ->join(["{$prefix}user_role" => 'ur'], 'rp.role_id = ur.role_id') 130 | ->where('ur.user_id', $userId)->select(); 131 | return $permission; 132 | } 133 | 134 | /** 135 | * 获取权限节点 136 | * @param $condition 137 | * @return array|\PDOStatement|string|\think\Collection|\think\Model|null 138 | * @throws \think\db\exception\DataNotFoundException 139 | * @throws \think\db\exception\ModelNotFoundException 140 | * @throws \think\exception\DbException 141 | */ 142 | public function getPermission($condition) 143 | { 144 | $model = Db::name('permission')->setConnection($this->getConnection()); 145 | if (is_numeric($condition)) { 146 | return $model->where('id', $condition)->find(); 147 | } else { 148 | return $model->where($condition)->select(); 149 | } 150 | } 151 | } -------------------------------------------------------------------------------- /src/model/Role.php: -------------------------------------------------------------------------------- 1 | 5 | * Date: 2019-04-17 6 | * Time: 22:51 7 | */ 8 | 9 | namespace gmars\rbac\model; 10 | 11 | 12 | use think\Db; 13 | use think\Exception; 14 | 15 | class Role extends Base 16 | { 17 | /** 18 | * 编辑角色 19 | * @param string $permissionIds 20 | * @param array $data 21 | * @return $this 22 | * @throws Exception 23 | * @throws \think\exception\PDOException 24 | */ 25 | public function saveRole($permissionIds = '', $data = []) 26 | { 27 | if (!empty($data)) { 28 | $this->data($data); 29 | } 30 | $validate = new \gmars\rbac\validate\Role(); 31 | if (!$validate->check($this)) { 32 | throw new Exception($validate->getError()); 33 | } 34 | $data = $this->getData(); 35 | if (isset($data['id']) && !empty($data['id'])) { 36 | $this->isUpdate(true); 37 | } 38 | $this->startTrans(); 39 | if ($this->save() === false) { 40 | $this->rollback(); 41 | throw new Exception('写入角色时出错'); 42 | } 43 | //如果有权限的情况下 44 | if (empty($permissionIds)) { 45 | $this->commit(); 46 | return $this; 47 | } 48 | $permissionIdsArr = array_filter(explode(',', $permissionIds)); 49 | if (empty($permissionIdsArr)) { 50 | $this->commit(); 51 | return $this; 52 | } 53 | //删除原有权限 54 | $rolePermission = new RolePermission($this->connection); 55 | if ($rolePermission->where('role_id', $this->id)->delete() === false) { 56 | $this->rollback(); 57 | throw new Exception('删除原有权限时出错'); 58 | } 59 | $writeData = []; 60 | foreach ($permissionIdsArr as $v) 61 | { 62 | $writeData[] = [ 63 | 'role_id' => $this->id, 64 | 'permission_id' => $v 65 | ]; 66 | } 67 | if ($rolePermission->saveAll($writeData) === false) { 68 | $this->rollback(); 69 | throw new Exception('写入角色权限时出错'); 70 | } 71 | $this->commit(); 72 | return $this; 73 | } 74 | 75 | /** 76 | * 删除角色 77 | * @param $condition 78 | * @return bool 79 | * @throws Exception 80 | * @throws \think\exception\PDOException 81 | */ 82 | public function delRole($condition) 83 | { 84 | $where = []; 85 | $relationWhere = []; 86 | if (is_array($condition)) { 87 | $where[] = ['id', 'IN', $condition]; 88 | $relationWhere[] = ['role_id', 'IN', $condition]; 89 | } else { 90 | $id = (int)$condition; 91 | if (is_numeric($id) && $id > 0) { 92 | $where[] = ['id', '=', $id]; 93 | $relationWhere[] = ['role_id', '=', $condition]; 94 | } else { 95 | throw new Exception('删除条件错误'); 96 | } 97 | } 98 | $this->startTrans(); 99 | if ($this->where($where)->delete() === false) { 100 | $this->rollback(); 101 | throw new Exception('删除角色出错'); 102 | } 103 | $rolePermission = new RolePermission($this->connection); 104 | if ($rolePermission->where($relationWhere)->delete() === false) { 105 | $this->rollback(); 106 | throw new Exception('删除角色关联权限出错'); 107 | } 108 | $this->commit(); 109 | return true; 110 | } 111 | 112 | /** 113 | * 获取角色列表 114 | * @param $condition 115 | * @param bool $withPermissionId 116 | * @return array|\PDOStatement|string|\think\Collection|\think\Model|null 117 | * @throws \think\db\exception\DataNotFoundException 118 | * @throws \think\db\exception\ModelNotFoundException 119 | * @throws \think\exception\DbException 120 | */ 121 | public function getRole($condition, $withPermissionId = false) 122 | { 123 | $model = Db::name('role')->setConnection($this->getConnection()); 124 | $where = []; 125 | if (is_array($condition)) { 126 | $where = $condition; 127 | } else { 128 | $condition = (int)$condition; 129 | if (is_numeric($condition) && $condition > 0) { 130 | $role = $model->where('id', $condition)->find(); 131 | if (!empty($role) && $withPermissionId) { 132 | $role['permission_ids'] = Db::name('role_permission')->setConnection($this->getConnection()) 133 | ->where('role_id', $condition)->column('permission_id'); 134 | } 135 | return $role; 136 | } 137 | } 138 | $role = Db::name('role')->setConnection($this->getConnection()) 139 | ->where($where)->select(); 140 | if (!empty($role) && $withPermissionId) { 141 | $permission = Db::name('role_permission')->setConnection($this->getConnection()) 142 | ->where('role_id', 'IN', array_column($role, 'id'))->select(); 143 | $roleIdIndexer = []; 144 | if (!empty($permission)) { 145 | foreach ($permission as $v) 146 | { 147 | $roleIdIndexer[$v['role_id']][] = $v['permission_id']; 148 | } 149 | } 150 | foreach ($role as &$v) 151 | { 152 | $v['permission_ids'] = isset($roleIdIndexer[$v['id']])? $roleIdIndexer[$v['id']] : []; 153 | unset($v); 154 | } 155 | } 156 | return $role; 157 | } 158 | 159 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tp5-rbac 2 | >本扩展包是tp5的rbac包,使用了部分tp5的特性实现了关系型数据库中特殊数据结构的处理。 3 | 4 | ## 安装方法 5 | 先安装composer如果不知道怎么安装使用composer请自行百度。 6 | 打开命令行工具切换到你的tp5项目根目录 7 | 8 | ``` 9 | composer require gmars/tp5-rbac 10 | ``` 11 | 如果该方法报错请按照以下方式操作: 12 | 13 | 1. 打开项目根目录下的composer.json 14 | 2. 在require中添加"gmars/tp5-rbac": "dev-master" 15 | 3. 运行composer update 16 | 17 | 添加后composer.json应该有这样的部分: 18 | 19 | ``` 20 | "require": { 21 | "php": ">=5.4.0", 22 | "topthink/framework": "^5.0", 23 | "gmars/tp5-rbac": "dev-master" 24 | }, 25 | ``` 26 | 27 | # v2.0+版本使用说明 28 | ## 配置 29 | 请将此配置加在config/app.php的配置中 30 | ```php 31 | 'rbac' => [ 32 | 'type' => 'jwt', //验证方式 jwt(token方式)形式或者service(基于cookie)方式 33 | 'db' => '', //rbac要使用的数据库配置为空则为默认库(生成表的前缀依赖此配置) 34 | 'salt_token' => 'asdfasfdafasf', //token加密密钥 35 | 'token_key' => 'Authorization' //header中用于验证token的名称 36 | ] 37 | ``` 38 | ## 使用说明 39 | 实例化rbac 40 | ```php 41 | $rbac = new Rbac(); 42 | ``` 43 | ### 管理操作 44 | #### 初始化rbac所需的表 45 | ```php 46 | //可传入参数$db为数据库配置项默认为空则为默认数据库(考虑到多库的情形) 47 | $rbac->createTable(); 48 | ``` 49 | 该方法会生成rbac所需要的表,一般只执行一次,为了安全,执行后会加锁,下次要执行需要删除锁文件再执行。 50 | 51 | #### 创建权限分组 52 | ```php 53 | $rbac->savePermissionCategory([ 54 | 'name' => '用户管理组', 55 | 'description' => '网站用户的管理', 56 | 'status' => 1 57 | ]); 58 | ``` 59 | 编辑和修改调用同一个方法编辑时请在参数中包含主键id的值 60 | 61 | #### 创建权限节点 62 | ```php 63 | $rbac->createPermission([ 64 | 'name' => '文章列表查询', 65 | 'description' => '文章列表查询', 66 | 'status' => 1, 67 | 'type' => 1, 68 | 'category_id' => 1, 69 | 'path' => 'article/content/list', 70 | ]); 71 | ``` 72 | - 如果为修改则在传入参数数组中加入主键id的键值 73 | - type为权限类型1为后端权限2为前端权限主要考虑到spa使用 74 | - category_id为上一步创建的权限分组的id 75 | - 创建成功返回添加的该条权限数据,错误抛出异常 76 | #### 创建角色&给角色分配权限 77 | ```php 78 | $rbac->createRole([ 79 | 'name' => '内容管理员', 80 | 'description' => '负责网站内容管理', 81 | 'status' => 1 82 | ], '1,2,3'); 83 | ``` 84 | - 如果修改请在第一个参数中传入主键的键值 85 | - 第二个参数为权限节点的id拼接的字符串请使用英文逗号 86 | 87 | #### 给用户分配角色 88 | ```php 89 | $rbac->assignUserRole(1, [1]); 90 | ``` 91 | - 该方法会删除用户之前被分配的角色 92 | - 第一个参数为用户id 93 | - 第二个参数为角色id的数组 94 | #### 获取权限分组列表 95 | ```php 96 | $rbac->getPermissionCategory([['status', '=', 1]]); 97 | ``` 98 | - 参数支持传入id查询单条数据和标准的where表达式查询列表传为空数组则查询所有 99 | 100 | #### 获取权限列表 101 | ```php 102 | $rbac->getPermission([['status', '=', 1]]); 103 | ``` 104 | - 参数支持传入id查询单条数据和标准的where表达式查询列表传为空数组则查询所有 105 | 106 | #### 获取角色列表 107 | ```php 108 | $rbac->getRole([], true); 109 | ``` 110 | - 第一个参数支持传入id查询单条数据和标准的where表达式查询列表传为空数组则查询所有 111 | - 第二个参数选择是否查询角色分配的所有权限id默认为true 112 | 113 | #### 删除权限分组 114 | ```php 115 | $rbac->delPermissionCategory([1,2,3,4]); 116 | ``` 117 | - 参数支持传入单个id或者id列表 118 | 119 | #### 删除权限 120 | ```php 121 | $rbac->delPermission([1,2,3,4]); 122 | ``` 123 | - 参数支持传入单个id或者id列表 124 | 125 | #### 删除角色 126 | ```php 127 | $rbac->delRole([1,2,3,4]); 128 | ``` 129 | - 参数支持传入单个id或者id列表 130 | - 删除角色会删除给角色分配的权限[关联关系] 131 | 132 | ### 验证操作 133 | #### service方式 134 | service方式因为要用到session一般要依赖于cookie。在用户登录后要获取用户权限操作 135 | ```php 136 | $rbac->cachePermission(1); 137 | ``` 138 | - 传入参数为登录用户的user_id 139 | - 该方法会返回该用户所有的权限列表 140 | 141 | 用户请求时进行验证 142 | ```php 143 | $rbac->can('article/channel/list'); 144 | ``` 145 | - 如果有权限返回true如果没有权限返回false 146 | #### jwt方式 147 | jwt方式在前后端分离结构用的比较普遍。在用户登录后需要获取token 148 | ```php 149 | $rbac->generateToken(1); 150 | ``` 151 | - 第一个参数为登录的用户id 152 | - 第二个参数为token有效期默认为7200秒 153 | - 第三个参数为token前缀 154 | 返回结果为 155 | ```php 156 | array(3) { 157 | ["token"] => string(32) "4c56b80f06d3d8810b97db33a1291694" 158 | ["refresh_token"] => string(32) "17914241bde6bfc46b20e643b2c58279" 159 | ["expire"] => int(7200) 160 | } 161 | ``` 162 | *使用refresh_token刷新权限* 163 | ```php 164 | $rbac->refreshToken('17914241bde6bfc46b20e643b2c58279'); 165 | ``` 166 | 请在有效期内使用refresh_token来刷新授权 167 | *用户请求时验证* 168 | ```php 169 | $rbac->can('article/channel/list'); 170 | ``` 171 | 172 | 173 | # < v2.0使用说明 174 | ## `数据迁移(可选,可以直接使用包中的gmars_rbac.sql文件导入)` 175 | 在使用本插件之前需要有rbac锁需要的数据库。在迁移之前如果你的数据库中已有user数据表那么请你备份自己的user数据表后删除。 176 | 177 | 在你的项目的某个config.php中加入如下配置: 178 | ```php 179 | 'migration' => [ 180 | 'path' => ROOT_PATH .'vendor/gmars/tp5-rbac/' 181 | ], 182 | ``` 183 | 然后把命令行切换到你的项目根目录Windows是cmd运行如下命令 184 | 185 | ```php 186 | php think migrate:run 187 | ``` 188 | 如果迁移运行成功会在你的数据库中生成如下几张表: 189 | ```php 190 | user 用户表 191 | user_role 用户角色对应表 192 | role 角色表 193 | role_permission 角色权限对应表 194 | permission 角色表 195 | ``` 196 | ### 使用该插件--RBAC的管理 197 | 198 | 在一个系统中RBAC是基于角色的权限控制。作为开发人员需要明白这是两个不同的过程。第一个就是构建系统的RBAC结构,包括添加权限,角色,用户,用户角色对应关系,角色权限对应关系等。 199 | 200 | 在此先说明RBAC管理: 201 | 202 | 1.添加用户 203 | 204 | 这一步是在用户注册时要做的一步,就是讲注册的用户添加到user表中。 205 | 206 | ```php 207 | $rbacObj = new Rbac(); 208 | $data = ['user_name' => 'zhangsan', 'status' => 1, 'password' => md5('zhangsan')]; 209 | $rbacObj->createUser($data); 210 | ``` 211 | 创建用户时传入唯一一个参数必须是数组。数组中应该包含用户表需要的数据。如果出现其他非user表的字段则会抛出异常。 212 | 该方法返回的结果为false或者Exception或者**新添加用户的id**。 213 | 214 | 2.添加权限 215 | 216 | 这一步是构建系统的权限。一般我们是以请求的路由为权限的识别标志。在该插件中使用path字段。 217 | 218 | 例如我们的系统中有商品列表这样的一个操作需要授权。 219 | 220 | 其路由为 /index/goods/list 221 | 222 | 添加路由如下: 223 | ```php 224 | $rbacObj = new Rbac(); 225 | $data = [ 226 | 'name' => '商品列表', 227 | 'status' => 1, 228 | 'description' => '查看商品的所有列表', 229 | 'path' => '/index/goods/list', 230 | 'create_time' => time() 231 | ]; 232 | $rbacObj->createPermission($data); 233 | ``` 234 | 3.添加角色 235 | 236 | 在RBAC的角色中角色是有父子关系的,也就是说所添加的角色可以是另一个角色的子角色。 237 | 238 | ```php 239 | $rbacObj = new Rbac(); 240 | $data = [ 241 | 'name' => '商品管理员', 242 | 'status' => 1, 243 | 'description' => '商品管理员负责商品的查看修改删除等操作', 244 | 'sort_num' => 10, 245 | 'parent_id' => 1 246 | ]; 247 | $rbacObj->createRole($data); 248 | ``` 249 | 250 | 需要注意的是在data中有个字段为parent_id这个字段标识了所要添加的角色的父角色。如果留为空则便是添加的父角色。 251 | 252 | 4.为用户分配角色 253 | 254 | 当然一个用户可以有多个角色。一般是使用多选框或其他形式选择后以数组的方式传入的。 255 | 256 | 例如: 257 | 258 | ```php 259 | $rbacObj = new Rbac(); 260 | $rbacObj->assignUserRole(1, [1, 2]); 261 | ``` 262 | 263 | assignUserRole($userId, array $roleArray = []) 264 | 265 | 该方法的第一个参数为用户id第二个参数是一个一位数组,其元素为角色的id 266 | 267 | 5.为角色分配权限 268 | 269 | 例如: 270 | ```php 271 | $rbacObj = new Rbac(); 272 | $rbacObj->assignRolePermission(1, [1, 2]); 273 | ``` 274 | 将id分别为1,2的权限分配给id为1的角色 275 | 276 | 6.删除角色 277 | >删除角色的同时必须删除角色和权限的对应数据 278 | 279 | ```php 280 | $rbacObj = new Rbac(); 281 | $rbacObj->delRole(1); 282 | ``` 283 | 其中需要传入的是角色id 284 | 285 | 7.将一个角色移到另一个角色下 286 | 287 | 以上已经说明了角色是有父子关系的那么肯定能够移动其位置 288 | 289 | ```php 290 | $rbacObj = new Rbac(); 291 | $rbacObj->moveRole(1,3); 292 | ``` 293 | 该例子是将id为1的角色移动到id为3的角色下作为子角色。 294 | 295 | >还有其他修改删除等方法的文档日后再补全,功能是有的 296 | 297 | ### 使用该插件--RBAC权限验证 298 | 299 | #### 登录后获取权限列表 300 | 如果自己写权限验证则请忽略这一步,如果要使用rbac插件来验证权限则必须要这样做。 301 | 302 | 在登录成功后做如下操作: 303 | 304 | ```php 305 | $rbacObj = new Rbac(); 306 | $rbacObj->cachePermission(1); 307 | ``` 308 | 这个方法是查询id为1的用户的所有权限并且以path索引后存入cache 309 | 310 | #### 请求时的权限验证 311 | 312 | 当然对于每一个方法都要进行权限验证时我们一般是在某一个父类中定义一个方法进行权限验证,验证如下: 313 | 314 | ```php 315 | $rbacObj = new Rbac(); 316 | $rbacObj->can('/index/goods/list'); 317 | ``` 318 | 319 | 该方法是验证当前用户有没有操作/index/goods/list的权限,如果有则返回true如果无则返回false 320 | 321 | 其中can的参数可以使用tp5的特性获取。 322 | -------------------------------------------------------------------------------- /src/Rbac.php: -------------------------------------------------------------------------------- 1 | 5 | * GitHub: https://github.com/gmars 6 | * Blog: http://blog.csdn.net/marswill 7 | * Date: 2017/8/21 8 | * Time: 上午12:38 9 | */ 10 | 11 | namespace gmars\rbac; 12 | 13 | 14 | use gmars\nestedsets\NestedSets; 15 | use gmars\rbac\model\Permission; 16 | use gmars\rbac\model\PermissionCategory; 17 | use gmars\rbac\model\Role; 18 | use gmars\rbac\model\UserRole; 19 | use think\Db; 20 | use think\db\Query; 21 | use think\db\Where; 22 | use think\Exception; 23 | use think\facade\Cache; 24 | use think\facade\Request; 25 | use think\facade\Session; 26 | 27 | class Rbac 28 | { 29 | 30 | 31 | /** 32 | * 认证方式 service方式和jwt方式 33 | * @var string 34 | */ 35 | private $type = "service"; 36 | 37 | /** 38 | * rbac存放的数据库 39 | * @var string 40 | */ 41 | private $db = ''; 42 | 43 | /** 44 | * jwt token生成加密密钥 45 | * @var string 46 | */ 47 | private $saltToken = 'asdfqet9#$@#GS#$%080asdfaasdg'; 48 | 49 | /** 50 | * token名称 51 | * @var string 52 | */ 53 | private $tokenKey = 'Authorization'; 54 | 55 | public function __construct() 56 | { 57 | $rbacConfig = config('rbac'); 58 | if (!empty($rbacConfig)) { 59 | isset($rbacConfig['db']) && $this->db = $rbacConfig['db']; 60 | isset($rbacConfig['type']) && $this->type = $rbacConfig['type']; 61 | isset($rbacConfig['salt_token']) && $this->saltToken = $rbacConfig['salt_token']; 62 | isset($rbacConfig['token_key']) && $this->tokenKey = $rbacConfig['token_key']; 63 | } 64 | 65 | } 66 | 67 | /** 68 | * 生成所需的数据表 69 | * @param string $db 70 | */ 71 | public function createTable($db = '') 72 | { 73 | $createTable = new CreateTable(); 74 | $createTable->create($db); 75 | } 76 | 77 | /** 78 | * 配置参数 79 | * @param string $db 80 | */ 81 | public function setDb($db = '') 82 | { 83 | $this->db = $db; 84 | } 85 | 86 | 87 | /** 88 | * 创建权限 89 | * @param array $data 90 | * @return Permission 91 | * @throws Exception 92 | */ 93 | public function createPermission(array $data = []) 94 | { 95 | $model = new Permission($this->db); 96 | $model->data($data); 97 | try{ 98 | $res = $model->savePermission(); 99 | return $res; 100 | } catch (Exception $e) { 101 | throw new Exception($e->getMessage()); 102 | } 103 | } 104 | 105 | /** 106 | * 修改权限数据(版本兼容暂时保留建议使用createPermission方法) 107 | * @param array $data 108 | * @param null $id 109 | * @return Permission 110 | * @throws Exception 111 | */ 112 | public function editPermission(array $data = [], $id = null) 113 | { 114 | if (!empty($id)) { 115 | $data['id'] = $id; 116 | } 117 | try{ 118 | return $this->createPermission($data); 119 | } catch (Exception $e) { 120 | throw new Exception($e->getMessage()); 121 | } 122 | } 123 | 124 | /** 125 | * 根据主键删除权限(支持多主键用数组的方式传入) 126 | * @param int $id 127 | * @return bool 128 | * @throws Exception 129 | */ 130 | public function delPermission($id = 0) 131 | { 132 | $model = new Permission($this->db); 133 | try { 134 | return $model->delPermission($id); 135 | } catch (Exception $e) { 136 | throw new Exception($e->getMessage()); 137 | } 138 | } 139 | 140 | /** 141 | * 根据条件删除权限条件请参考tp5 where条件的写法 142 | * @param $condition 143 | * @return bool 144 | * @throws Exception 145 | * @throws \think\exception\PDOException 146 | */ 147 | public function delPermissionBatch($condition) 148 | { 149 | $model = new Permission($this->db); 150 | if ($model->where($condition)->delete() === false) { 151 | throw new Exception('批量删除数据出错'); 152 | } 153 | return true; 154 | } 155 | 156 | /** 157 | * 根据主键/标准条件来查询权限 158 | * @param $condition 159 | * @return array|\PDOStatement|string|\think\Collection|\think\Model|null 160 | * @throws \think\db\exception\DataNotFoundException 161 | * @throws \think\db\exception\ModelNotFoundException 162 | * @throws \think\exception\DbException 163 | */ 164 | public function getPermission($condition) 165 | { 166 | $model = new Permission($this->db); 167 | return $model->getPermission($condition); 168 | } 169 | 170 | /** 171 | * 编辑权限分组 172 | * @param array $data 173 | * @return PermissionCategory 174 | * @throws Exception 175 | */ 176 | public function savePermissionCategory(array $data = []) 177 | { 178 | $model = new PermissionCategory($this->db); 179 | $model->data($data); 180 | try{ 181 | $res = $model->saveCategory(); 182 | return $res; 183 | } catch (Exception $e) { 184 | throw new Exception($e->getMessage()); 185 | } 186 | } 187 | 188 | /** 189 | * 根据主键删除权限分组(支持多主键用数组的方式传入) 190 | * @param int $id 191 | * @return bool 192 | * @throws Exception 193 | */ 194 | public function delPermissionCategory($id = 0) 195 | { 196 | $model = new PermissionCategory($this->db); 197 | try { 198 | $res = $model->delCategory($id); 199 | return $res; 200 | } catch (Exception $e) { 201 | throw new Exception($e->getMessage()); 202 | } 203 | } 204 | 205 | /** 206 | * 获取权限分组 207 | * @param $where 208 | * @return array|\PDOStatement|string|\think\Collection|\think\Model|null 209 | * @throws \think\db\exception\DataNotFoundException 210 | * @throws \think\db\exception\ModelNotFoundException 211 | * @throws \think\exception\DbException 212 | */ 213 | public function getPermissionCategory($where) 214 | { 215 | $model = new PermissionCategory($this->db); 216 | return $model->getCategory($where); 217 | } 218 | 219 | /** 220 | * 编辑角色 221 | * @param array $data 222 | * @param string $permissionIds 223 | * @return Role 224 | * @throws Exception 225 | */ 226 | public function createRole(array $data = [], $permissionIds = '') 227 | { 228 | $model = new Role($this->db); 229 | $model->data($data); 230 | try{ 231 | $res = $model->saveRole($permissionIds); 232 | return $res; 233 | } catch (Exception $e) { 234 | throw new Exception($e->getMessage()); 235 | } 236 | 237 | } 238 | 239 | /** 240 | * 根据id或标准条件获取角色 241 | * @param $condition 242 | * @param bool $withPermissionId 243 | * @return array|\PDOStatement|string|\think\Collection|\think\Model|null 244 | * @throws \think\db\exception\DataNotFoundException 245 | * @throws \think\db\exception\ModelNotFoundException 246 | * @throws \think\exception\DbException 247 | */ 248 | public function getRole($condition, $withPermissionId = true) 249 | { 250 | $model = new Role($this->db); 251 | return $model->getRole($condition, $withPermissionId); 252 | } 253 | 254 | /** 255 | * @param $id 256 | * @return bool 257 | * @throws Exception 258 | * 删除角色同时将角色权限对应关系删除(注意,会删除角色分配的权限关联数据) 259 | */ 260 | public function delRole($id) 261 | { 262 | $model = new Role($this->db); 263 | try { 264 | $res = $model->delRole($id); 265 | return $res; 266 | } catch (Exception $e) { 267 | throw new Exception($e->getMessage()); 268 | } 269 | } 270 | 271 | 272 | /** 273 | * @param $userId 274 | * @param array $role 275 | * @return int|string 276 | * @throws Exception 277 | * 为用户分配角色 278 | */ 279 | public function assignUserRole($userId, array $role = []) 280 | { 281 | if (empty($userId) || empty($role)) { 282 | throw new Exception('参数错误'); 283 | } 284 | $model = new UserRole($this->db); 285 | $model->startTrans(); 286 | if ($model->where('user_id', $userId)->delete() === false) { 287 | $model->rollback(); 288 | throw new Exception('删除用户原有角色出错'); 289 | } 290 | $userRole = []; 291 | foreach ($role as $v) 292 | { 293 | $userRole [] = ['user_id' => $userId, 'role_id' => $v]; 294 | } 295 | if ($model->saveAll($userRole) === false) { 296 | $model->rollback(); 297 | throw new Exception('给用户分配角色出错'); 298 | } 299 | $model->commit(); 300 | return ; 301 | } 302 | 303 | /** 304 | * 删除用户角色 305 | * @param $id 306 | * @return bool 307 | * @throws Exception 308 | * @throws \think\exception\PDOException 309 | */ 310 | public function delUserRole($id) 311 | { 312 | if (empty($id)) { 313 | throw new Exception('参数错误'); 314 | } 315 | $model = new UserRole($this->db); 316 | if ($model->where('user_id', $id)->delete() === false) { 317 | throw new Exception('删除用户角色出错'); 318 | } 319 | return true; 320 | } 321 | 322 | /** 323 | * 获取用户权限并缓存 324 | * @param $id 325 | * @param int $timeOut 326 | * @return array|bool|mixed|\PDOStatement|string|\think\Collection 327 | * @throws Exception 328 | * @throws \think\db\exception\DataNotFoundException 329 | * @throws \think\db\exception\ModelNotFoundException 330 | * @throws \think\exception\DbException 331 | */ 332 | public function cachePermission($id, $timeOut = 3600) 333 | { 334 | if (empty($id)) { 335 | throw new Exception('参数错误'); 336 | } 337 | $model = new Permission($this->db); 338 | $permission = $model->userPermission($id, $timeOut); 339 | return $permission; 340 | } 341 | 342 | /** 343 | * @param $path 344 | * @return bool 345 | * @throws Exception 346 | * 检查用户有没有权限执行某操作 347 | */ 348 | public function can($path) 349 | { 350 | if ($this->type == 'jwt') { 351 | $token = Request::header($this->tokenKey); 352 | if (empty($token)) { 353 | throw new Exception('未获取到token'); 354 | } 355 | $permissionList = Cache::get($token); 356 | } else { 357 | //获取session中的缓存名 358 | $cacheName = Session::get('gmars_rbac_permission_name'); 359 | if (empty($cacheName)) { 360 | throw new Exception('未查询到登录信息'); 361 | } 362 | $permissionList = Cache::get($cacheName); 363 | } 364 | 365 | if (empty($permissionList)) { 366 | throw new Exception('您的登录信息已过期请重新登录'); 367 | } 368 | 369 | if (isset($permissionList[$path]) && !empty($permissionList[$path])) { 370 | return true; 371 | } 372 | return false; 373 | } 374 | 375 | /** 376 | * 生成jwt的token 377 | * @param $userId 378 | * @param int $timeOut 379 | * @param string $prefix 380 | * @return array 381 | * @throws \think\db\exception\DataNotFoundException 382 | * @throws \think\db\exception\ModelNotFoundException 383 | * @throws \think\exception\DbException 384 | */ 385 | public function generateToken($userId, $timeOut = 7200, $prefix = '') 386 | { 387 | $token = md5($prefix . $this->randCode(32) . $this->saltToken . time()); 388 | $freshTOken = md5($prefix . $this->randCode(32) . $this->saltToken . time()); 389 | $permissionModel = new Permission($this->db); 390 | $permission = $permissionModel->getPermissionByUserId($userId); 391 | //无权限时为登录验证用 392 | if (!empty($permission)) { 393 | $newPermission = []; 394 | if (!empty($permission)) { 395 | foreach ($permission as $k=>$v) 396 | { 397 | $newPermission[$v['path']] = $v; 398 | } 399 | } 400 | Cache::set($token, $newPermission, $timeOut); 401 | } else { 402 | //权限为空时token仅仅用作登录身份验证 403 | Cache::set($token, '', $timeOut); 404 | } 405 | Cache::set($freshTOken, $token, $timeOut); 406 | return [ 407 | 'token' => $token, 408 | 'refresh_token' => $freshTOken, 409 | 'expire' => $timeOut 410 | ]; 411 | } 412 | 413 | /** 414 | * 刷新token 415 | * @param $refreshToken 416 | * @param int $timeOut 417 | * @param string $prefix 418 | * @return array 419 | * @throws Exception 420 | */ 421 | public function refreshToken($refreshToken, $timeOut = 7200, $prefix = '') 422 | { 423 | $token = Cache::get($refreshToken); 424 | if (empty($token)) { 425 | throw new Exception('refresh_token已经过期'); 426 | } 427 | $permission = Cache::get($token); 428 | if (empty($permission)) { 429 | throw new Exception('token已经过期'); 430 | } 431 | $token = md5($prefix . $this->randCode(32) . $this->saltToken . time()); 432 | $freshTOken = md5($prefix . $this->randCode(32) . $this->saltToken . time()); 433 | Cache::set($token, $permission, $timeOut); 434 | Cache::set($freshTOken, $token); 435 | return [ 436 | 'token' => $token, 437 | 'refresh_token' => $freshTOken, 438 | 'expire' => $timeOut 439 | ]; 440 | 441 | } 442 | 443 | /** 444 | * 生成随机码 445 | * @param int $length 446 | * @param string $type string|mix|number|special 447 | * @return string 448 | */ 449 | private function randCode($length = 6, $type = 'mix') 450 | { 451 | $number = '0123456789'; 452 | $seed = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; 453 | $specialChar = '!@#$%^&*()_+[]|'; 454 | $randRes = ""; 455 | switch ($type) { 456 | case 'string': 457 | for ($i = 0; $i < $length; $i++) 458 | { 459 | $randomInt = rand(0, strlen($seed) - 1); 460 | $randRes .= $seed{$randomInt}; 461 | } 462 | break; 463 | case 'number': 464 | for ($i = 0; $i < $length; $i++) 465 | { 466 | $randomInt = rand(0, strlen($number) - 1); 467 | $randRes .= $number{$randomInt}; 468 | } 469 | break; 470 | case 'mix': 471 | $mix = $number . $seed; 472 | for ($i = 0; $i < $length; $i++) 473 | { 474 | $randomInt = rand(0, strlen($mix) - 1); 475 | $randRes .= $mix{$randomInt}; 476 | } 477 | break; 478 | case 'special': 479 | $special = $number . $seed . $specialChar; 480 | for ($i = 0; $i < $length; $i++) 481 | { 482 | $randomInt = rand(0, strlen($special) - 1); 483 | $randRes .= $special{$randomInt}; 484 | } 485 | break; 486 | } 487 | return $randRes; 488 | } 489 | 490 | /** 491 | * @param $roleId 492 | * @param array $permission 493 | * @return int|string 494 | * @throws Exception 495 | * 为角色分配权限 496 | */ 497 | public function assignRolePermission($roleId, array $permission = []) 498 | { 499 | throw new Exception('该方法已经弃用,请在创建角色时分配权限,调用createRole方法。如果你得项目中依旧想使用此方法请安装v1.3.1版本'); 500 | } 501 | 502 | /** 503 | * 移动角色 504 | * @param $id 505 | * @param $parentId 506 | * @throws Exception 507 | */ 508 | public function moveRole($id, $parentId) 509 | { 510 | throw new Exception('新版本中已经弃用角色的可继承关系,请使用用户可分配多个角色替代,如果你得项目中依旧想使用此方法请安装v1.3.1版本'); 511 | } 512 | 513 | /** 514 | * 修改角色数据 515 | * @param $data 516 | * @return int|string 517 | * @throws Exception 518 | */ 519 | public function editRole($data) 520 | { 521 | throw new Exception('请使用createRole方法在data中传入主键,如果你得项目中依旧想使用此方法请安装v1.3.1版本'); 522 | } 523 | 524 | /** 525 | * @param array $data 526 | * @return int|string 527 | * @throws Exception 528 | * 创建用户[建议在自己系统的业务逻辑中实现] 529 | */ 530 | public function createUser(array $data = []) 531 | { 532 | throw new Exception('该方法在新版本中已经废弃,因为用户表的差异比较大请大家自行实现'); 533 | } 534 | 535 | } --------------------------------------------------------------------------------