PHP和RBAC设计思想、数据表设计及源码讲解
权限系统模块对于互联网产品来说是非常重要的功能。可以控制不同用户对不同资源的合理访问,实现安全访问
权限控制有哪些模型
- ACL
- RBAC基于角色的访问控制
从上图可以看出,ACL是与用户和权限直接相关的,而RBAC是通过角色间接与权限相关的。所以我们意识到角色是RBAC系统的一个重要属性。
什么是RBAC模型
RBAC(Role-Based Access Control,基于角色的访问控制)是指通过角色将用户与权限连接起来。简单来说,一个用户有多个角色,每个角色有不同的权限。这样就构建了“用户-角色-权威”的授权模型。在该模型中,用户与用户、用户与权限之间的关系一般是多对多的关系。
为什么选择RBAC模型
原因如下:
- 方便用户分组
- 方便权限分配和回收
- 易于扩展,可以满足大部分业务需求。管理,首先要知道权限管理必须是功能性的。图中
RBAC模型关系图
,分别是:
1个用户属性(张三、李四、王五)
2角色属性(业务员、卖家、前台)
3用户与角色的关系(张三是销售经理,李四、王五是销售)
4 权限(添加客户、编辑客户、删除客户、查看客户)
5 权限与角色的关系(销售有查看客户的权限,销售经理可以查看/添加/删除/编辑客户)RBAC 权限模块必须实现三个功能 角色 管理 角色 列表
添加 角色
更改 角色
添加 角色 权限 - 权限管理
权限列表 权限列表 新建 更改权限
如何如图所示! [](https://codeqd.com/zb_users/upload/2023/09/v2-17728d7bc856d34ebef9d66f77305f8e_720w.jpg)
数据表设计
用户表Pininer表
CREATE TABLE `user_role` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`uid` int(11) NOT NULL DEFAULT '0' COMMENT '用户id',
`role_id` int(11) NOT NULL DEFAULT '0' COMMENT '角色ID',
`created_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '插入时间',
PRIMARY KEY (`id`),
KEY `idx_uid` (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户角色表';
复制代码
权限明细表
CREATE TABLE `access` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(50) NOT NULL DEFAULT '' COMMENT '权限名称',
`urls` varchar(1000) NOT NULL DEFAULT '' COMMENT 'json 数组',
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态 1:有效 0:无效',
`updated_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '最后一次更新时间',
`created_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '插入时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='权限详情表';
复制代码
角色的权限表用户操作记录表
CREATE TABLE `app_access_log` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`uid` bigint(20) NOT NULL DEFAULT '0' COMMENT '品牌UID',
`target_url` varchar(255) NOT NULL DEFAULT '' COMMENT '访问的url',
`query_params` longtext NOT NULL COMMENT 'get和post参数',
`ua` varchar(255) NOT NULL DEFAULT '' COMMENT '访问ua',
`ip` varchar(32) NOT NULL DEFAULT '' COMMENT '访问ip',
`note` varchar(1000) NOT NULL DEFAULT '' COMMENT 'json格式备注字段',
`created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_uid` (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户操作记录表';
复制代码
代码实现
本系统所有页面均需登录后才能访问。框架添加统一验证方式
public function beforeAction($action) {
$login_status = $this->checkLoginStatus();
if ( !$login_status && !in_array( $action->uniqueId,$this->allowAllAction ) ) {
if(Yii::$app->request->isAjax){
$this->renderJSON([],"未登录,请返回用户中心",-302);
}else{
$this->redirect( UrlService::buildUrl("/user/login") );//返回到登录页面
}
return false;
}
//保存所有的访问到数据库当中
$get_params = $this->get( null );
$post_params = $this->post( null );
$model_log = new AppAccessLog();
$model_log->uid = $this->current_user?$this->current_user['id']:0;
$model_log->target_url = isset( $_SERVER['REQUEST_URI'] )?$_SERVER['REQUEST_URI']:'';
$model_log->query_params = json_encode( array_merge( $post_params,$get_params ) );
$model_log->ua = isset( $_SERVER['HTTP_USER_AGENT'] )?$_SERVER['HTTP_USER_AGENT']:'';
$model_log->ip = isset( $_SERVER['REMOTE_ADDR'] )?$_SERVER['REMOTE_ADDR']:'';
$model_log->created_time = date("Y-m-d H:i:s");
$model_log->save( 0 );
/**
* 判断权限的逻辑是
* 取出当前登录用户的所属角色,
* 在通过角色 取出 所属 权限关系
* 在权限表中取出所有的权限链接
* 判断当前访问的链接 是否在 所拥有的权限列表中
*/
//判断当前访问的链接 是否在 所拥有的权限列表中
if( !$this->checkPrivilege( $action->getUniqueId() ) ){
$this->redirect( UrlService::buildUrl( "/error/forbidden" ) );
return false;
}
return true;
}
复制代码
检查是否有权限访问指定链接
public function checkPrivilege( $url ){
//如果是超级管理员 也不需要权限判断
if( $this->current_user && $this->current_user['is_admin'] ){
return true;
}
//有一些页面是不需要进行权限判断的
if( in_array( $url,$this->ignore_url ) ){
return true;
}
return in_array( $url, $this->getRolePrivilege( ) );
}
复制代码
获取某个用户的所有权限,移除指定用户的权限,通过角色移除权限关系,移除所有权限链接权限表中
public function getRolePrivilege($uid = 0){
if( !$uid && $this->current_user ){
$uid = $this->current_user->id;
}
if( !$this->privilege_urls ){
$role_ids = UserRole::find()->where([ 'uid' => $uid ])->select('role_id')->asArray()->column();
if( $role_ids ){
//在通过角色 取出 所属 权限关系
$access_ids = RoleAccess::find()->where([ 'role_id' => $role_ids ])->select('access_id')->asArray()->column();
//在权限表中取出所有的权限链接
$list = Access::find()->where([ 'id' => $access_ids ])->all();
if( $list ){
foreach( $list as $_item ){
$tmp_urls = @json_decode( $_item['urls'],true );
$this->privilege_urls = array_merge( $this->privilege_urls,$tmp_urls );
}
}
}
}
return $this->privilege_urls ;
}
作者:PHP架构师圈
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。