ThinkPHP框架使用心得三 RBAC权限控制(1)使用

  最近有点小忙,好久没有学习了。。。我忏悔。。。。

  RBAC英文全称Role-Based Access Control。即基于角色的权限控制。它的权限控制原理是将个项目-模块-操作的使用权限分配给角色组。然后将用户分到各用户组中,当然,一个用户可能有多个用户组。这样用户从属于用户组后就具有了该用户组的相关操作权限。ThinkPHP中有一个封装的很好的RBAC类库,十分好用的说。同时官方也提供的demo包中也有rbac的例子,不过,据说好多人看着头疼。我之前也拿这个例子来看,发现。。。超越了我的理解啊。。。。现在我对rbac的原理大体了解后来看它的demo,我知道问题所在了,它的demo写的太好。对于我这种刚学习的人而言,绝大部分的精力会不由自主的放进他的操作流程,而会忽略最重要的数据表之间的操作,从而很可能难以理解rbac的原理。

  我自己写了一个rbac的demo,最初的工作是利用curd编写一个对文章的增改查的程序,连用户登录都免了,简要代码:

1 class IndexAction extends Action {
2     public function index() {}  //文章列表
3     public function add() {}  //新增文章
4     public function edit() {}  //文章编辑
5     public function view() {}  //文章查看
6 }

实现的效果:

一个简单的系统搭建完成了,下面加入权限控制。

RBAC的实现至少需要五张数据表,(我使用tp默认的前缀think_)分别为:

thinkl_user 用户表
字段 类型 说明
id int(11)  pk
username varchar(50)  
password varchar(50)  
thinkl_access 权限表
字段 类型 说明
role_id smallint(6)  角色id
node_id smallint(6)  节点id
level tinyint  表示所属层次,项目=〉1,模块=〉2,操作=〉3
module varchar(50)  
thinkl_node 节点表
字段 类型 说明
id smallint(6)  pk
name varchar(20)  
title varchar(50)  
status tinyint  
remark varchar(255)  
sort smallint(6)  
pid smallint(6)  
level tinyint  
thinkl_role角色表
字段 类型 说明
id smallint(6)  pk
name varchar(20)  
pid smallint(6)  
status tinyint  
remark varchar(255)  
thinkl_role_user 角色用户关联表
字段 类型 说明
role_id smallint(6)  
user_id smallint(6)  

 RBAC认证流程图

rbac的原理其实就是这几张表的数据逻辑关系。我是手动建立相关数据,一遍数据建立下来,逻辑理清了,基本原理也能理解的不差了。

(一)建立用户表数据

建立三个用户,分别为system,admin,user1

(二)建立角色组

建立两个角色组,分别为admin,user,即管理组和普通组

(三)建立用户和用户组的关系

 将system,admin划归角色组1

将user1划归角色组2

这样组合用户的对应关系就建立好了

(4)建立节点表

所谓节点就是所有的项目、模块、操作的列表。这张表应该算rbac的核心。

我的demo中项目名rabc,有一个模块即IndexAction,下面还有index,add,edit,view四个操作。将这6条分别计入这张表中,注意pid的设置。同时level的值,1、2、3分别代表项目,模块,操作。

(5)建立权限表

对于权限的分配就在access表中。

表中数据的意思是role_id=1的组,即admin组具有操作节点1,2,3,4,6的权限,不具备操作节点5即rbac-Index-edit的权限。role_id=2的组即用户组织具有节点123的操作权限。

很重要的一点就是,权限的安排需要按层次来,即只有具备对项目的操作权限才能操作模块,同理,只有具备对项目-模块的操作权限才能操作下面的方法。

这样,rbac的操作从数据表层面来讲已经完成。下面对代码进行修改以完成rbac的权限控制。

 首先,将RBAC.class.php复制到项目目录Lib\Org下,也可以直接使用系统目录Lib\ORG\Util下的类库文件,只要能够import即可。

然后,在项目配置文件中定义rbac的相关配置项:

//rbac配置项
    'USER_AUTH_ON'              =>true,
    'USER_AUTH_TYPE'        =>2,        // 默认认证类型 1 登录认证 2 实时认证
    'USER_AUTH_KEY'             =>'authId',    // 用户认证SESSION标记
    'ADMIN_AUTH_KEY'        =>'administrator',
    'USER_AUTH_MODEL'           =>'User',    // 默认验证数据表模型
    'AUTH_PWD_ENCODER'          =>'md5',    // 用户认证密码加密方式
    'USER_AUTH_GATEWAY'         =>'/Public/login',// 默认认证网关
    'NOT_AUTH_MODULE'           =>'Public',    // 默认无需认证模块
    'REQUIRE_AUTH_MODULE'       =>'',        // 默认需要认证模块
    'NOT_AUTH_ACTION'           =>'',        // 默认无需认证操作
    'REQUIRE_AUTH_ACTION'       =>'',        // 默认需要认证操作
    'GUEST_AUTH_ON'             =>false,    // 是否开启游客授权访问
    'GUEST_AUTH_ID'             =>0,        // 游客的用户ID
    'SHOW_RUN_TIME'             =>true,        // 运行时间显示
    'SHOW_ADV_TIME'             =>true,        // 显示详细的运行时间
    'SHOW_DB_TIMES'             =>true,        // 显示数据库查询和写入次数
    'SHOW_CACHE_TIMES'          =>true,        // 显示缓存操作次数
    'SHOW_USE_MEM'              =>true,        // 显示内存开销
    'DB_LIKE_FIELDS'            =>'title|remark',
    'RBAC_ROLE_TABLE'           =>'think_role',
    'RBAC_USER_TABLE'           =>'think_role_user',
    'RBAC_ACCESS_TABLE'         =>'think_access',
    'RBAC_NODE_TABLE'           =>'think_node',

这些配置项可以直接从tp的rbacdemo中复制。每项的意思也基本都注明了。不需要说太多了。

再然后定义一个PublicAction用来放置一些不需要进行认证的模块。如果修改了配置项中NOT_AUTH_MODULE,那么就建立相应名称的action。这里面放置登录,登出,检验登录情况等操作。如果用户连登录都发现没有权限,这是个多么疯狂的情况,用户想登录一直说:你丫无权操作,一边凉快去。多抓狂。

代码结构:

class PublicAction extends Action{

    // 用户登录页面
    public function login() {
        if (!isset($_SESSION[C('USER_AUTH_KEY')])) {
            $this->display();
        } else {
            $this->redirect('Index/index');
        }
    }

    // 登录检测
    public function checkLogin() {
        
    }

    function loginout() {
        if (isset($_SESSION[C('USER_AUTH_KEY')])) {
            unset($_SESSION[C('USER_AUTH_KEY')]);
            unset($_SESSION);
            session_destroy();
            $this->assign("jumpUrl", __URL__ . '/login/');
            $this->success('登出成功!');
        } else {
            $this->error('已经登出!');
        }
    }

}

login,logout就是判断session时候存在。存在就认为已经登录,执行页面跳转或者删掉这个session以达到退出效果。

checklogin()是rbac的具体实现

public function checkLogin() {
        if (empty($_POST['username'])) {
            $this->error('帐号错误!');
        } elseif (empty($_POST['password'])) {
            $this->error('密码必须!');
        } 
        //生成认证条件
        $map = array();
        // 支持使用绑定帐号登录
        $map['username'] = $_POST['username'];
        import('ORG.Util.RBAC');
        $authInfo = RBAC::authenticate($map);
        //使用用户名、密码和状态的方式进行认证
        if (false === $authInfo) {
            $this->error('帐号不存在或已禁用!');
        } else {
            if ($authInfo['password'] != md5($_POST['password'])) {
                $this->error('密码错误!');
            }
            $_SESSION[C('USER_AUTH_KEY')] = $authInfo['id'];
            if ($authInfo['username'] == 'system') {
                $_SESSION['administrator'] = true;
            }
            
            // 缓存访问权限
            RBAC::saveAccessList();
            $this->success('登录成功!');
        }
    }

首先判断提交过来的表单信息,然后引入rbac类,然后调用RBAC::authenticate($map);来获取认证信息。然后根据认证信息来判断用户时候登录成功以及具有的权限。

再然后编写一个BaseAction.class.php,这里放置一个自动方法,就是每次执行这个action时,这个方法会像构造函数一样自动执行。这个方法就是检查用户权限。

function _initialize() {
        // 用户权限检查
        if (C ( 'USER_AUTH_ON' ) && !in_array(MODULE_NAME,explode(',',C('NOT_AUTH_MODULE')))) {
            import ( '@.Org.RBAC' );
            if (! RBAC::AccessDecision ()) {
                //检查认证识别号
                if (! $_SESSION [C ( 'USER_AUTH_KEY' )]) {
                    //跳转到认证网关
                    redirect ( PHP_FILE . C ( 'USER_AUTH_GATEWAY' ) );
                }
                // 没有权限 抛出错误
                if (C ( 'RBAC_ERROR_PAGE' )) {
                    // 定义权限错误页面
                    redirect ( C ( 'RBAC_ERROR_PAGE' ) );
                } else {
                    if (C ( 'GUEST_AUTH_ON' )) {
                        $this->assign ( 'jumpUrl', PHP_FILE . C ( 'USER_AUTH_GATEWAY' ) );
                    }
                    // 提示错误信息
                    $this->error ( L ( '_VALID_ACCESS_' ) );
                }
            }
        }
    }

然后修改IndexAction使它由Action改为继承至BaseAction,这样每个页面的执行都会执行自动方法。以后如果增加新的模块也让它继承BaseAction这样就实现了权限控制了。

以上简单的权限控制就完成了。代码基本可以从官方例子中复制稍作修改即可。下面看看效果。

 结果不太好表示,按之前对表的编辑,结合实际调试,使用system登录时,虽然他属于admin组,而admin组并不具备编辑的权限,但是由于它是系统管理员,判定具有所有权限。用admin登录可以发现只有编辑链接点击时无权操作。使用user1登录,除了能看首页,其他什么事不能干。

就此,RBAC算是结束了。至于官方的demo,是可以再页面上配置各种值,说白了就是将我的手动编辑表的过程在界面上实现。能够理解原理,下面就可以试着做出官方demo那样便捷的操作界面。

源代码,希望大神指正。

原文地址:https://www.cnblogs.com/listenRain/p/rbac.html