• 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那样便捷的操作界面。

    源代码,希望大神指正。

  • 相关阅读:
    01 Jq 基础
    01 属性
    input 编辑框 光标 的相关问题
    登录页面 文字 2 3 4 个字 等宽俩端对齐 空格
    复选框单选框与文字对齐问题的研究与解决
    在Python中对MySQL中的数据进行可视化
    输入2个坐标的数值求出对应坐标间的距离和圆的面积
    numpy中arange函数内起始值必须大于结束值,否则生成为空的一维数组
    条件语句练习-比分预测
    三元表达式是棒棒哒!
  • 原文地址:https://www.cnblogs.com/listenRain/p/rbac.html
Copyright © 2020-2023  润新知