ThinkPHP框架下基于RBAC的权限控制模式详解

时间:2021-10-10 06:10:08

  这几天因为要做一个项目,需要可以对Web应用中通用功能进行封装,其中一个很重要的涉及到了对用户、角色和权限部分的灵活管理。所以基于TP框架自己封装了一个对操作权限和菜单权限进行灵活配置的可控制模式。

  RBAC角色权限分配模式大家或许都不陌生,其重要的访问控制原理就是将权限基于角色进行动态分配,在一个工作模式中,每个人都被分配了不同的角色,因为每个人角色的不同所以每个人负责的事情也不同。最简单的举一个例子来说吧,就一个大学而言,一个大学分为校长、某个学院的院长、某个系的系主任,他们每个人都有不同的角色,校长要做可能就是宏观把控整个学校的大方向,院长就是对自己这个学院负责,系主任呢就是应该对自己系里面的各种具体的事情进行处理和监督。从例子中,大家可能也已经看出来了,在整个的工作组织中,由上而下分为了很多不同的角色,而且每个不同角色的人承担的责任也不同,所以我们在我们的灵活可配置的控制模式中应该做到对不同级别、不同角色的用户的操作权限进行动态配置,这样就不用我们写大量的重复代码,节省了我们大量的时间。

  其实,在TP框架中也自带了一个角色权限分配的封装代码,可是我自己不太想用它的,所以自己去封装吧。

  在RBAC的权限分配模式中,我们所有的权限都是基于角色,所以我们应该有一个非常重要的可以左右联系的表->role表(角色表)。现在我们有了角色表,可是我们最终的服务对象还是我们的用户,真正使角色有意义的是用户,因为是某个用户具有某个角色,所以我们还应该有一个基本的用户表->user表(用户表)。现在角色左边的用户表有了,那么还缺少一个什么呢?那就是我们的权限表,因为我们不同的操作对应的是是否具有该操作的执行权限,所以我们还应该有一个基本的权限表->access表(权限表)。现在有了这些表,我们还应该将这些表联系起来,那就是用户角色表->user_role表和角色权限表->role_access表。上面这五个表就是RBAC模式中最基本的5张表。但是现在我还想能够将菜单也做成灵活可定制的,所以还需要有一个menu表。其实到了这里,就是出现一个分歧,那就是我们菜单和角色的关联,现在有两个途径,一个就是将菜单也当成用户的操作权限,将菜单作为一个权限放在权限表中去为角色分配。另外一个就是在扩展一个角色菜单表role_menu表,我们将菜单级别的权限配置专门进行管理,做菜单的灵活配置。

  所有的表有了,接下来就是做到如何才能对操作权限和菜单权限进行控制呢?这就需要针对不同的底层框架去实现了,在TP框架中,自己一开始想说去用行为类实现。我想专门去实现了一个检查权限的行为类,在每次执行操作的时候都去检查是否有该操作权限,若没有则给出提示,并停止执行代码。虽然整体的设计没有错,可是自己在实现时,发现我们每次都要去用B方法调用该行为类,让它去执行run()方法,这样显然不方便。后来自己好好想了想,发现刚才自己的想法太蠢了,其实自己完全没有必要去用行为类做,仅仅是一个控制器就完全可以实现。

  具体的做法是这样,我封装了一个基类的AdminController(因为主要是针对后台的),让所有后台应用的控制器都去继承自AdminController,在每个控制器的构造函数中都去继承AdminController的构造函数,这样我每次的请求在执行时都会去初始化AdminController中的构造函数,然后我在基类的构造函数中去截获当前执行操作的控制器和对应的方法,然后去对比操作权限,如果存在则“放行”,如果没有,那就不好意思,给出一个提示哈。

  菜单也是相似的操作。不过我在控制器中设置了对admin管理员的自动过滤,让admin自动拥有所有的菜单和权限。

  经过以上的操作,我们就实现了对权限的控制,并且是对菜单级别和按钮级别均有控制。

  好了,接下来奉上在权限判断部分的代码,供大家参考。

  

 public function __construct(){
parent::__construct();
if(session("?loginuser")){ //表示当前人已经登录,则我们应该获取
$loginuser=session("loginuser"); //获取当前登录人的session值
if($loginuser!="admin"){ //只有在当前登录人不是admin的时候,我们才需要去获取当前人执行的请求方法,然后进行权限对比
$con_name=CONTROLLER_NAME;
$fun_name=ACTION_NAME;
$url=$con_name."/".$fun_name;
$access_urls=session("access_urls"); //将所有目前session中的权限获取出来 //echo "<script>alert('".$url."')</script>";
if($fun_name!="ajaxIndex"){ //echo "<script>alert('".$url."')</script>";
if($url=="Index/index"||$url=="Index/head"||$url=="Index/left"||$url=="Index/right"){ //我们默认给出显示首页的三个方法的权限 }
else{
$is_have="error";
foreach ($access_urls as $key => $value) {
# code...
if(in_array($url, $value)){
$is_have="ok";
break;
}
} if($is_have=="error"){ //若最终为error,则表示当前没有该权限
echo "<script>alert('您不具备该权限!')</script>";
die();
}
}
}
} }
else{
$this->redirect("Login/showLogin");
}

  好了,上述部分就是一个简单的权限控制模式,并且是到按钮级别哟。

  在补充一句吧,上述只是基于TP框架的封装。其它具体框架可以具体操作,比如Laravel框架,在Laravel中新推出了一个叫中间件的东西,在这个中间件中所有的操作都会经过这个中间件,所以,我们就可以在这个中间件中做非常多的事情啦。

  好了,到此结束,也写上那句话吧:此文属于博主原创,如果转载,请标明出处哟。