## 一、RBAC 参考链接:([https://blog.csdn.net/a549654065/article/details/108883147](https://blog.csdn.net/a549654065/article/details/108883147))
RBAC: role base access control 基于角色的用户[访问权限](https://so.csdn.net/so/search?q=%E8%AE%BF%E9%97%AE%E6%9D%83%E9%99%90&spm=1001.2101.3001.7020)控制权限,就是权限分配给角色,角色又分配给用户。
即**一个用户对应一个角色,一个角色对应多个权限**,一个用户对应用户组,一个用户组对应多个权限。
![](https://img.kancloud.cn/f8/1c/f81c2845bf279cfe9093345fa35d999f_576x209.png)
## 二、认证授权逻辑
登录逻辑: ![](https://img.kancloud.cn/12/10/1210d1e7e8b94aa3fdd84ca50100e782_443x612.png)
权限控制逻辑:![](https://img.kancloud.cn/5e/5b/5e5bf11b13a695d947b57bcc99f70ad8_593x443.png)
## 三、具体实现
### 创建表
```
CREATE TABLE `users` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`role_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '角色ID',
`name` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '账号',
`truename` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '未知' COMMENT '账号',
`password` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '密码',
`email` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '邮箱',
`phone` varchar(15) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '手机号码',
`photo` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '头像',
`sex` enum('先生','女士') COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '先生' COMMENT '性别',
`last_ip` char(15) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '登录IP',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `roles` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '角色名称',
`tag` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '角色标识',
`status` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '0未引用,1已引用',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `nodes` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '节点名称',
`route_name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '路由别名,权限认证标识',
`route_tag` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`pid` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '上级ID',
`is_menu` enum('0','1') COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '是否是菜单',
`sort` int(11) DEFAULT NULL COMMENT '排序',
`status` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '0未引用,1已引用',
`deleted_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=18 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `role_nodes` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`role_id` int(11) DEFAULT NULL COMMENT '角色',
`node_id` int(11) DEFAULT NULL COMMENT '节点',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=39 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
```
Model文件
```
class User extends Authenticatable
{
public function role()
{
return $this->belongsTo(Role::class,'role_id');
}
}
class Role extends Model
{
public function nodes()
{
// 参1 关联模型
// 参2 中间表的表名,没有前缀
// 参3 本模型对应的外键ID
// 参4 关联模型对应的外键ID
return $this->belongsToMany(
Node::class,
'role_node',
'role_id',
'node_id');
}
}
class Node extends Model
{
/**
* 获取所有权限节点
*
* @return array
*/
public function getAllList(){
$data = self::get()->toArray();
return $this->treeLevel($data);
}
/**
* 数组的合并,并加上html标识前缀
*
* @param array $data
* @param int $pid
* @param string $html
* @param int $level
* @return array
*/
public function treeLevel(array $data, int $pid = 0, string $html = '--', int $level = 0) {
static $arr = [];
foreach ($data as $val) {
if ($pid == $val['pid']) {
// 重复一个字符多少次
$val['html'] = str_repeat($html, $level * 2);
$val['level'] = $level + 1;
$arr[] = $val;
$this->treeLevel($data, $val['id'], $html, $val['level']);
}
}
return $arr;
}
/**
* 数据多层级
*
* @param array $data
* @param int $pid
* @return array
*/
public function subTree(array $data, int $pid = 0) {
// 返回的结果
$arr = [];
foreach ($data as $val) {
// 给定的PID是当前记录的上级ID
if ($pid == $val['pid']) {
// 递归
$val['sub'] = $this->subTree($data,$val['id']);
$arr[] = $val;
}
}
return $arr;
}
}
```
### 修改路由文件
新增角色、节点、用户列表
```
Route::post('/admin/user/login', 'AuthController@login')->name('admin.index');
Route::get('/admin/user/logout', 'AuthController@logout')->name('admin.logout');
Route::group(['prefix' => '/admin','middleware' => 'adminAuth','as' => 'admin.'], function () {
// 角色列表
Route::group(['prefix' => 'role','as' => 'role.'], function () {
Route::get('', 'RoleController@search')->name('search');
Route::get('/{id}', 'RoleController@show')->where('id', '\d+')->name('show');
Route::put('/{id}', 'RoleController@update')->where('id', '\d+')->name('update');
Route::post('/', 'RoleController@store')->name('store');
Route::delete('/{id}', 'RoleController@destroy')->where('id', '\d+')->name('destroy');
// 获取某一角色的权限列表
Route::get('/node/{id}', 'RoleController@nodeList')->name('node');
// 更新某一角色的权限列表
Route::post('/node/{role}', 'RoleController@saveNode')->name('node');
});
// 节点列表
Route::group(['prefix' => 'node','as' => 'node.'], function () {
Route::get('', 'NodeController@search')->name('search');
Route::get('/{id}', 'NodeController@show')->where('id', '\d+')->name('show');
Route::put('/{id}', 'NodeController@update')->where('id', '\d+')->name('update');
Route::post('/', 'NodeController@store')->name('store');
Route::delete('/{id}', 'NodeController@destroy')->where('id', '\d+')->name('destroy');
});
// 用户列表
Route::group(['prefix' => 'user','as' => 'user.'], function () {
Route::get('', 'UserController@search')->name('search');
Route::get('/{id}', 'UserController@show')->where('id', '\d+')->name('show');
Route::put('/{id}', 'UserController@update')->where('id', '\d+')->name('update');
Route::post('/', 'UserController@store')->name('store');
Route::delete('/{id}', 'UserController@destroy')->where('id', '\d+')->name('destroy');
});
});// end-auth
```
路由别名
命名规则: 格式为XXX.YYY.ZZZ,以角色接口为例,假设路由接口为 api/admin/role/search ,则设置该路由的别名为 admin.role.search,以此类推, admin.node.search、admin.role.update等
设置路由别名主要是为了权限鉴定的时候方便处理对路由的鉴权。
### 中间件过滤
创建中间件
```
php artisan make:middleware AdminAuthenticated
```
在`Kernel.php`文件里`$routeMiddleware`新增
```
// 后台
'adminAuth' => \App\Http\Middleware\AdminAuthenticated::class,
```
白名单
考虑到业务本身的原因,这里新增一个权限白名单,在`config`目录下创建rbac.php文件,配置用户白名单以及路由白名单,以便后续业务的延申拓展。
```
<?php
return [
// 超级管理员
"super" => 'admin',
// 默认允许通过的路由
"allow_route" => [
'admin.index',
'admin.logout'
]
];
```
中间件过滤:
```
//第一种
public function handle($request, Closure $next, $guard = null)
{
if (!auth()->check()){
return response()->json(['code'=>401, 'msg' => '您未登录!']);
}
$allow_node = session('admin.auth.'.Auth::id());
$auths = is_array($allow_node) ? array_filter($allow_node):[];
// 合并数组
$auths = array_merge($auths, config('rbac.allow_route'));
// 当前访问的路由
$currentRoute = $request->route()->getName();
$request->auths = $auths;
// 权限判断
if (auth()->user()->username != config('rbac.super') &&
!in_array($currentRoute, $auths)){
return response()->json(['code' => 400, 'msg' => '您没有权限访问']);
}
return $next($request);
}
```
```
public function handle($request, Closure $next)
{
$role\_id\=getRole(); //获取角色Id,公共方法
$info\=Role::with('nodes')->where('id',$role\_id)->first();
$info\=$info ? $info\->toArray() : \[\];
//当前角色的节点{
if(!empty($info)){
$nodeInfo\=$info\['nodes'\];
foreach($nodeInfo as $key\=>$val){
if($val\['is\_menu'\]==1){
$urls\[\]=$val\['route\_name'\];
}
}
}
// 合并数组
$auths = array\_merge($urls, config('rbac.allow\_route'));
//dd( $auths);
// 当前访问的路由
$currentRoute ='/'.request()->path();
//dd( $auths);
// 权限判断
if ($info\['tag'\] != config('rbac.super') && !in\_array($currentRoute, $auths)){
//return view('admin.error');
// return JsonReturnError('400','您没有权限访问','');
return response()->json(\[
'code' => 1003,
'message' => 'No Promission' , //token已过期
'data'\=>''
\]);
}
return $next($request);
}
```
- 重要文档
- 工作须知
- 项目下的公共方法
- 程序员给代码写注释时有哪些讲究!
- RestFul 讲解
- 其他知识
- docker容器
- MongoDB的使用
- Node.js的使用
- Session共享
- Wordpress的使用
- Websocket的简介
- Ajax
- 项目技术
- Https配置证书
- Nginx的反向代理
- MySQL读写分离配置(laravel篇)
- Nginx的负载均衡
- App接口返回格式
- laravel中JWT的应用
- laravel验证码的使用
- laravel公共方法文件
- laravel框架的RBAC
- Git相关
- Git篇1
- Git篇2
- Mysql相关
- Mysql的主从复制
- MySQL的备份
- MySQL的使用
- 请求第三方接口
- Redis相关
- redis的使用
- Redis的基本用法以及场景分析
- 开发小技巧
- Linux
- 禁用root及密码登录
- Lnmp环境的安装
- 安装composer
- 安装Redis
- 文件共享服务 samba
- 其他安装
- Lnmp常用命令
- 性能检测命令
- Nginx的配置详解
- PHP相关
- PHP基础知识
- php常见的系统函数
- PHP的设计模式
- Cookie 和 Session 的封装
- Mysql知识
- Mysql索引
- MySQL的数据类型
- PHP重要知识
- PHP框架篇
- Laravel框架
- laravel---Excel
- laravel文件上传
- Laravel-Mysql常用操作
- Laravel队列(queue)
- laravel-发送Email
- laravel--JWT
- TP框架篇
- tp5主从数据库设置读写分离
- 前端
- JS
- js代码实现点击按钮出现60秒倒计时
- 开发软件相关
- 代码编辑器
- vs code配置ftp连接远程服务器实现代码文自动上传
- 编程相关软件下载