一个字段解决RBAC权限。RBAC傻瓜版改造。

jerry thinkphp 2015年11月18日 收藏
官方的权限系统太复杂了,初学者往往云里雾里,搞不清楚。
这是本人的傻瓜版改造。请各位品鉴。
官方的rbac。本人看了诸多文章,虽然最后搞明白了,但是耗费的脑细胞的确不少。而且要数据库支持。用起来实在不便于和自己的系统集成。好坏暂且不论,单从繁琐上而言,的确不算完美。
自己琢磨了一个傻瓜版,简单说一下吧。欢迎拍砖。
权限系统的原理就不详细解说了。总之要点就这几个:
1.用户表,记录具体操作人员账号。
userid
username
2.角色表,或者叫用户组表。记录系统可以使用的角色。
roleid
rolename
funstr(关键)
其中有一个字段:权限串funstr。要用varchar(2000)或更大,是个大字符字段,存储角色的具体权限点信息。下面会解释怎么用。
3.用户角色关联表。把用户和角色关联起来。
再次基础上有无数变形。再此就不在说了,基本就是这三个。
ok,帽子戏法开始:
有看官会问;权限表呢?缺少记录系统权限点的表啊。
这就是傻瓜版的要点,
新建一个类,暂起名fun吧。
其中建一个array,保存系统的所有module的所有class的所有function。
就是系统的所有操作方法。这个array是这样的:
class AdmFunarrAction extends Action
{
    
    /////////////////////////////////////////////////////////////////////////////
    /////////////////////////网站所有功能的array/////////////////////////////////  
 
   //主键:GROUP-class-function用于权限检查时定位
   //值为另一个array,存储具体对应function功能的相关信息
     
     //1.id--权限点对应的字符编码(字母开头,后跟两位字母和数字)
     //     是进行权限检查的依据

     //2.pid--父权限点对应的字符编码,构造权限树时使用  

     //3.n--name--权限点名称 

     //4.t--tree--是否用来构造树节点,存入权限串中
     //值1--构造树节点,存入权限串。     
     //值0--不用构造树节点,不存入权限串,只用来检查,
     //     处理几个fun合并用一个id的情况,比如各种查询合并成一个id  
  
     //5.l--login--是否需要登录,强行控制,保证安全,0--无需登录 1--必须登录
     
     //6.x--xx--权限点详细说明      
     
     //7.p--pic --权限点的树形图标 
    /////////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////    
  //-----总后台所有模块的权限数组
    PUBLIC $admfunarr = array(
//     主键                            |        标号      |父级标号     |名称                         |节点属性| 强制登陆|详细                          |图标             
'aaaa-0000-0000'                      =>array('id'=>'a','pid'=>'aaaa',  'n'=>'总后台',                  't'=>'1','l'=>'0','x' => '总后台根节点','p'=>'icon-add'),
'Admin-Main-index'                    =>array('id'=>'a01','pid'=>'a',   'n'=>'后台登陆页',              't'=>'1','l'=>'0','x' => '机构后台管理的登陆页面','p'=>'icon-add'),
'Admin-Main-main'                     =>array('id'=>'a02','pid'=>'a',   'n'=>'后台首页',                't'=>'1','l'=>'1','x' => '机构后台管理的主页面','p'=>'icon-add'),                         
  'aaaa-0000-0001'                      =>array('id'=>'a03','pid'=>'a02', 'n'=>'基础设置',              't'=>'1','l'=>'1','x' => '基础设置菜单','p'=>'icon-add'),      
    'Admin-Adminuser-index'               =>array('id'=>'a04','pid'=>'a03', 'n'=>'管理员维护',          't'=>'1','l'=>'1','x' => '管理员维护,包括增删改查','p'=>'icon-add'),       
      'Admin-Adminuser-getlist'             =>array('id'=>'a04','pid'=>'a04', 'n'=>'合-管理员列表',     't'=>'0','l'=>'1','x' => '总后台管理员管理','p'=>'icon-add'),       
      'Admin-Adminuser-getone'              =>array('id'=>'a04','pid'=>'a04', 'n'=>'合-管理员单个查询', 't'=>'0','l'=>'1','x' => '总后台管理员管理','p'=>'icon-add'),       
      'Admin-Adminuser-addfun'              =>array('id'=>'a04','pid'=>'a04', 'n'=>'合-管理员新增',     't'=>'0','l'=>'1','x' => '总后台管理员管理','p'=>'icon-add'),       
      'Admin-Adminuser-updatefun'           =>array('id'=>'a04','pid'=>'a04', 'n'=>'合-管理员修改',     't'=>'0','l'=>'1','x' => '总后台管理员管理','p'=>'icon-add'),       
      'Admin-Adminuser-delfun'              =>array('id'=>'a04','pid'=>'a04', 'n'=>'合-管理员删除',     't'=>'0','l'=>'1','x' => '总后台管理员管理','p'=>'icon-add'),       
  'aaaa-0000-0002'                      =>array('id'=>'a05','pid'=>'a02', 'n'=>'高级设置',              't'=>'1','l'=>'1','x' => '基础设置菜单','p'=>'icon-add'),      
    'Admin-Corp-index'               =>array('id'=>'a06','pid'=>'a05', 'n'=>'高级管理员维护',      't'=>'1','l'=>'1','x' => '管理员维护,包括增删改查','p'=>'icon-add'),       
      'Admin-Adminuser-getlist1'             =>array('id'=>'a06','pid'=>'a06', 'n'=>'合-管理员列表',     't'=>'0','l'=>'1','x' => '总后台管理员管理','p'=>'icon-add'),       
      'Admin-Adminuser-getone1'              =>array('id'=>'a06','pid'=>'a06', 'n'=>'合-管理员单个查询', 't'=>'0','l'=>'1','x' => '总后台管理员管理','p'=>'icon-add'),       
      'Admin-Adminuser-addfun1'              =>array('id'=>'a06','pid'=>'a06', 'n'=>'合-管理员新增',     't'=>'0','l'=>'1','x' => '总后台管理员管理','p'=>'icon-add'),       
      'Admin-Adminuser-updatefun1'           =>array('id'=>'a06','pid'=>'a06', 'n'=>'合-管理员修改',     't'=>'0','l'=>'1','x' => '总后台管理员管理','p'=>'icon-add'),       
      'Admin-Adminuser-delfun1'              =>array('id'=>'a06','pid'=>'a06', 'n'=>'合-管理员删除',     't'=>'0','l'=>'1','x' => '总后台管理员管理','p'=>'icon-add'),                       
        
        
        
        
        
        );   
这是一个复合array,array的每一项也都是一个array。
array的主键是GROUP-class-function三个合成的。别说你不知道这三个的意思啊。
每个主键对应的值也是一个array,描述这个主键对应的function。有这么几个:
//1.id--权限点对应的字符编码,比如a01,a02之类
(字母开头,后跟两位字母和数字,可以重复)
这个是关键啊看条件需要时可以重复前面角色表权限字段(funstr)里面保存的就是用逗号分隔的这个funciton编码组合,
代表了这个角色可以拥有的全部操作。 是进行权限检查的依据!

------由于我是用ztree进行权限管理,下面这些附加属性只是为了构造ztree需要的json便于前台分配权限时操作。
除了name(功能的名称)必须,你完全可以忽略不用。但其中仍有精妙之处,请大家耐心。
//2.pid--父权限点对应的字符编码,构造权限树时使用(ztree需要,没什么好说的)

//3.n--name--权限点名称 (没什么好说的。你的function干什么用的总要起个名字吧)

//4.t--tree--是否用来构造树节点,存入权限串中(这个精妙了
//值1--构造树节点,存入权限串。
//值0--不用构造树节点,不存入权限串,只用来检查, 处理几个fun合并用一个id的情况,比如一个针对用户的增删改查合并成一个功能点id。
------这什么意思???
我们前面说了,每个function都要在这个数组中有一条记录。但是不是每个function
都代表一个功能点。有的时候权限控制比较粗,比如我只需要控制一个用户管理权限,有这个权限就可以进行用户的增删改查
的全部操作,不需要再细分增删改查这四个操作权限。这时就可以把增删改查的四个权限合并,设置一个虚拟的权限点,比如
上面的 'aaaa-0000-0001',他下面增加增删改查的方法,但是都用同一个编码。这就是编码重复的条件
这样,再检查权限时,就当做一个权限点进行检查了。一个权限点代表增删改查四个具体操作。
精妙吧.....呵呵呵,无耻!


//5.l--login--是否需要登录,硬编码强行控制,保证安全,即便无意误分派了权限,比如给游客分派了登录才能有的权限,
在这里也可以挡一下。0--无需登录 1--必须登录

//6.x--xx--权限点详细说明 (没什么说了)

//7.p--pic --权限点的树形图标 (没什么说了,ztree需要)


---------------------------------以上权限array解释完毕,如头晕,请移步草榴开心一下再来


有了这个你的系统的全部function的array,还需要权限点表吗?
而且,维护这样一个array,比维护一个权限点的表,不知道容易了多少倍。

接下来的权限判断就是老生长谈了,无非建一个基类,写一个init方法,你的其他类都从这里继承吧。
<?php
/*
 * 总后台公共模块的基础类
 */
class AdminCommonAction extends Action{
    /*
     * 初始化
     * 权限验证
     * Session判断
     */
    public function _initialize(){

      header ( 'Content-Type:text/html;charset=utf-8' );
        //是否登录判断,没有登录需要登录
        if (!session('?admuserid')){
               redirect(__APP__.'/'.GROUP_NAME.'/Login/index'); 
            }
        //获得session权限串
        $funstr='';
        if (session('?admuserfunstr')){
              $funstr=session('admuserfunstr');
            } 
        //判断当前访问是否在权限串中
          //1.获得权限数组,我的权限数组在一个专门类中,所以要在这里调用一下,也可以写在一起,完全无压力。
            $s=new AdmFunarrAction();
            $admfunarr=$s->admfunarr;//权限数组            
          //2.根据group,action,fun获得key
            $key=GROUP_NAME.'-'.MODULE_NAME.'-'.ACTION_NAME;//注意加‘-’啊。用‘-’进行连接
            if (!array_key_exists("$key",$admfunarr)){
                if (! IS_AJAX) {
                    header ( 'Content-Type:text/html;charset=utf-8' );
                    echo ('<h2 style="color:red;">失败555...!你可能没有权限啊,快升级吧...</h2>-------------------------<a href="javascript:history.go(-1);">返回</a>');
                    exit ();
                } else {
                    $json = '{"zt":0,"xx":"失败555...!你可能没有权限啊,快升级吧..."}';
                    echo $json;
                    exit ();
                }              
            }
          //3.根据key获得id   
            $id=$admfunarr["$key"]['id'];
          //4.根据id判断是否在权限字符串中
            $pos = strpos($funstr,$id);
            if (!$pos){
                if (! IS_AJAX) {
                    header ( 'Content-Type:text/html;charset=utf-8' );
                    echo ('<h2 style="color:red;">失败555555...!你可能没有权限啊,快升级吧...</h2>-------------------------<a href="javascript:history.go(-1);">返回</a>');
                    exit ();
                } else {
                    $json = '{"zt":0,"xx":"失败555555...!你可能没有权限啊,快升级吧..."}';
                    echo $json;
                    exit ();
                }                
            }    
         
    }
    
}
有了代码,其实没什么好说的了,唠叨一句:
.根据group,action,fun获得key,这三个tp都有常量的,获得了key,根据key找到系统功能array的对应值,从里面提取出id,
然后判断这个id在不在角色表的funstr串当中就可以判定是否可以操作了。

还少了一块,吧系统功能array的每个key对应的值中的id存入角色表的funstr当中。这个就不献丑了。随后有时间附上源代码吧。


周末闲来无事。小文一篇,蜻蜓点水而已,只是说个思路。欢迎各路大神拍啊拍啊拍!!!