ThinkPHP-RBAC

一、基本配置 
复制代码
  5     'USER_AUTH_ON'          =>true,             //开启认证  
  6     'USER_AUTH_TYPE'        =>1,                // 默认认证类型 1 登录认证 2 实时认证
  7     'USER_AUTH_KEY'         =>'authId',         // 用户认证SESSION标记
  8     'ADMIN_AUTH_KEY'        =>'administrator',
  9     'USER_AUTH_MODEL'       =>'User',           // 默认验证数据表模型           
 10     'AUTH_PWD_ENCODER'      =>'md5',            // 用户认证密码加密方式         
 11     'USER_AUTH_GATEWAY'     =>'/Public/login',  // 默认认证网关
 12     'NOT_AUTH_MODULE'       =>'Public',         // 默认无需认证模块
 13     'REQUIRE_AUTH_MODULE'   =>'',               // 默认需要认证模块             
 14     'NOT_AUTH_ACTION'       =>'',               // 默认无需认证操作             
 15     'REQUIRE_AUTH_ACTION'   =>'',               // 默认需要认证操作             
 16     'GUEST_AUTH_ON'         =>false,            // 是否开启游客授权访问         
 17     'GUEST_AUTH_ID'         =>0,                // 游客的用户ID
 18     'SHOW_RUN_TIME'         =>true,             // 运行时间显示
 19     'SHOW_ADV_TIME'         =>true,             // 显示详细的运行时间           
 20     'SHOW_DB_TIMES'         =>true,             // 显示数据库查询和写入次数     
 21     'SHOW_CACHE_TIMES'      =>true,             // 显示缓存操作次数             
 22     'SHOW_USE_MEM'          =>true,             // 显示内存开销
 23     'DB_LIKE_FIELDS'        =>'title|remark',   //开启like匹配
 24     'RBAC_ROLE_TABLE'       =>'ds_role',
 25     'RBAC_USER_TABLE'       =>'ds_role_user',
 26     'RBAC_ACCESS_TABLE'     =>'ds_power_role',
 27     'RBAC_POWER_TABLE'      =>'ds_power',
复制代码

注:我这里与ThinkPHP官网提供的源码有些区别,将

 

RBAC_NODE_TABLE改为了RBAC_POWER_TABLE,并且里面的数据库和字段名字也有些区别,所以要先将相关的变量、数据库和字段的名字该过来。(在VIM中可以用

 

:12,23s/

 

RBAC_NODE_TABLE/

 

RBAC_POWER_TABLE/g 将从12行到23行中出现的所有包含 

 

RBAC_NODE_TABLE的字符串中的 

 

RBAC_NODE_TABLE替换为 

 

RBAC_POWER_TABLE)。

1、第 7 行:配置了认证SESSION标记为authId,这个值随便配置,只要不系统中现有的$_SESSION 不冲突便可(例如:$_SESSION[‘USER_AUTH_KEY’] 相当于$_SESSION[‘authId’])

2、第 11 行:‘USER_AUTH_GATEWAY’=>’/Public/login’ // 也就是用户登录后台的一道门,若已经登录,则会直接跳转到后台主页面;相反,没有设置$_SESSION[‘authId’]这个值,或者已经过期,将会 显示登录页面;
3、PublicAction.class.php
checkUser   检查用户是否登录
top   
footer
menu
main
login
index
logout
checkLogin
changePwd
profile
verify
change
 
IndexAction.class.php
 
 

二、RBAC类中的 方法 

1、authenticate($map,$model='') //1.认证方法 2.使用给定的Map进行认证 3.传入查询用户的条件和用户表的MODEL 返回数组包含用户的信息 

// 认证方法
 84     static public function authenticate($map,$model='')                                   
 85     {   
 86         if(empty($model)) $model =  C('USER_AUTH_MODEL');                                 
 87         //使用给定的Map进行认证
 88         return M($model)->where($map)->find();                                            

 89     }  

 

2、saveAccessList($authId=null)//1.用于检测用户权限的方法,并保存到Session中  传入用户的ID 2.此方法不返回值,只是设置 $_SESSION['_ACCESS_LIST']的值,其中包含了所有该用户对应的用户组的有权限操作的所有节 点$_SESSION['_ACCESS_LIST']['项目名']['模块名']['操作名'],以后判断权限就是判断当前项目,模块和操作是否在 $_SESSION['_ACCESS_LIST']中能找到。

saveAccessList方法在Lib\ORG目录下RBAC.class.php文件里,登陆时checkLogin方法有导入这个文件。

 

复制代码
static function saveAccessList($authId=null)
    {
        if(null===$authId)   $authId = $_SESSION[C('USER_AUTH_KEY')];
        // 如果使用普通权限模式,保存当前用户的访问权限列表
        // 对管理员开发所有权限
        //USER_AUTH_TYPE代表ThinkPHP 权限的两种认证方式
         //2即时模式是指即时修改用户权限即时生效
         //1即时修改权限,修改完成后用户仍在系统中的话不生效。下次登陆时才会生效
        //如果不是管理员   权限认证也不是即时生效就会去获取用户权限并缓存

        if(C('USER_AUTH_TYPE') !=2 && !$_SESSION[C('ADMIN_AUTH_KEY')] )
            $_SESSION['_ACCESS_LIST']   =   RBAC::getAccessList($authId);
        return ;
    } 
复制代码

 

3、getRecordAccessList()// 取得模块的所属记录访问权限列表 返回有权限的记录ID数组

 static function getRecordAccessList($authId=null,$module='') {

104         if(null===$authId)   $authId = $_SESSION[C('USER_AUTH_KEY')];                     
105         if(empty($module))  $module =   MODULE_NAME;                                      
106         //获取权限访问列表
107         $accessList = RBAC::getModuleAccessList($authId,$module);                         
108         return $accessList;
109     }

 

4、checkAccess() 方法 检测当前模块和操作是否需要验证 返回bool类型

复制代码
static function checkAccess()
113     {
114         //如果项目要求认证,并且当前模块需要认证,则进行权限认证
115         if( C('USER_AUTH_ON') ){
116             $_module    =   array();
117             $_action    =   array();
118             if("" != C('REQUIRE_AUTH_MODULE')) {
119                 //需要认证的模块
120                 $_module['yes'] = explode(',',strtoupper(C('REQUIRE_AUTH_MODULE')));
121             }else {
122                 //无需认证的模块
123                 $_module['no'] = explode(',',strtoupper(C('NOT_AUTH_MODULE')));
124             }
125             //检查当前模块是否需要认证
126             if((!empty($_module['no']) && !in_array(strtoupper(MODULE_NAME),$_module['no'])) || (!empty($_module['yes']) &&            in_array(strtoupper(MODULE_NAME),$_module['yes']))) {
127                 if("" != C('REQUIRE_AUTH_ACTION')) {
128                     //需要认证的操作
129                     $_action['yes'] = explode(',',strtoupper(C('REQUIRE_AUTH_ACTION')));
130                 }else {
131                     //无需认证的操作
132                     $_action['no'] = explode(',',strtoupper(C('NOT_AUTH_ACTION')));
133                 }
134                 //检查当前操作是否需要认证
135                 if((!empty($_action['no']) && !in_array(strtoupper(ACTION_NAME),$_action['no'])) || (!empty($_action['yes']) &&        in_array(strtoupper(ACTION_NAME),$_action['yes']))) {
136                     return true;
137                 }else {
138                     return false;
139                 }
140             }else {
141                 return false;
142             }
143         }       
144         return false;
复制代码

145     }  

 

5、checkLogin()方法 检测登录

 148     static public function checkLogin() {

复制代码
149         //检查当前操作是否需要认证
150         if(RBAC::checkAccess()) {
151             //检查认证识别号
152             if(!$_SESSION[C('USER_AUTH_KEY')]) {
153                 if(C('GUEST_AUTH_ON')) {
154                     // 开启游客授权访问
155                     if(!isset($_SESSION['_ACCESS_LIST']))
156                         // 保存游客权限
157                         RBAC::saveAccessList(C('GUEST_AUTH_ID'));
158                 }else{
159                     // 禁止游客访问跳转到认证网关
160                     redirect(PHP_FILE.C('USER_AUTH_GATEWAY'));
161                 }
162             }
163         }
164         return true;
165     }
复制代码

 

6、AccessDecision($appName=APP_NAME) 方法 就是检测当前项目模块操作 是否在$_SESSION['_ACCESS_LIST']数组中,也就是说 在 $_SESSION['_ACCESS_LIST'] 数组中$_SESSION['_ACCESS_LIST']['当前操作']['当前模块']['当前操作']是否存在。如果存在表示有权限 否则返回flase。

复制代码
168     static public function AccessDecision($appName=APP_NAME)
169     {
170         //检查是否需要认证
171         if(RBAC::checkAccess()) {
172             //存在认证识别号,则进行进一步的访问决策
173             $accessGuid   =   md5($appName.MODULE_NAME.ACTION_NAME);
174             if(empty($_SESSION[C('ADMIN_AUTH_KEY')])) {
175                 if(C('USER_AUTH_TYPE')==2) {
176                     //加强验证和即时验证模式 更加安全 后台权限修改可以即时生效
177                     //通过数据库进行访问检查
178                     $accessList = RBAC::getAccessList($_SESSION[C('USER_AUTH_KEY')]);
179                 }else {
180                     // 如果是管理员或者当前操作已经认证过,无需再次认证
181                     if$_SESSION[$accessGuid]) {
182                         return true;
183                     }
184                     //登录验证模式,比较登录后保存的权限访问列表
185                     $accessList = $_SESSION['_ACCESS_LIST'];
186                 }
187                 //判断是否为组件化模式,如果是,验证其全模块名
188                 $module = defined('P_MODULE_NAME')?  P_MODULE_NAME   :   MODULE_NAME;
189                 if(!isset($accessList[strtoupper($appName)][strtoupper($module)][strtoupper(ACTION_NAME)])) {
190                     $_SESSION[$accessGuid]  =   false;
191                     return false;
192                 }
193                 else {
194                     $_SESSION[$accessGuid]  =   true;
195                 }
196             }else{
197                 //管理员无需认证
198                 return true;
199             }
200         }

201         return true;
复制代码

202     } 

 

7、getAccessList($authId) 方法 通过查询数据库 返回权限列表 $_SESSION['_ACCESS_LIST']的值,取得当前认证号的所有权限列表。

View Code
复制代码
213     static public function getAccessList($authId)
214 {
215 // Db方式权限数据
216 $db = Db::getInstance(C('RBAC_DB_DSN'));
217 $table = array('role'=>C('RBAC_ROLE_TABLE'),'user'=>C('RBAC_USER_TABLE'),'access'=>C('RBAC_ACCESS_TABLE'), 'power'=>C('RBAC_POWER_TABLE'));
218 $sql = "select power.id,power.name from ".
219 $table['role']." as role,".
220 $table['user']." as user,".
221 $table['access']." as access ,".
222 $table['power']." as power ".
223 "where user.user_id='{$authId}' and user.role_id=role.id and ( access.role_id=role.id or (access.role_id=role. role_pid and role.role_pid!=0 ) ) and role.status=1 and access.power_id=power.id and power.power_level=1 and power.status=1";
224 $apps = $db->query($sql);
225 $access = array();
226 foreach($apps as $key=>$app) {
227 $appId = $app['id'];
228 $appName = $app['name'];
229 // 读取项目的模块权限
230 $access[strtoupper($appName)] = array();
231 $sql = "select power.id,power.name from ".
232 $table['role']." as role,".
233 $table['user']." as user,".
234 $table['access']." as access ,".
235 $table['power']." as power ".
236 "where user.user_id='{$authId}' and user.role_id=role.id and ( access.role_id=role.id or (access.role_id=role. role_pid and role.role_pid!=0 ) ) and role.status=1 and access.power_id=power.id and power.power_level=2 and power. power_pid={$appId} and power.status=1";
237 $modules = $db->query($sql);
238 // 判断是否存在公共模块的权限
239 $publicAction = array();

240 foreach($modules as $key=>$module) {
241 $moduleId = $module['id'];
242 $moduleName = $module['name'];
243 if('PUBLIC'== strtoupper($moduleName)) {
244 $sql = "select power.id,power.name from ".
245 $table['role']." as role,".
246 $table['user']." as user,".
247 $table['access']." as access ,".
248 $table['power']." as power ".
249 "where user.user_id='{$authId}' and user.role_id=role.id and ( access.role_id=role.id or (access.role_id=role. role_pid and role.role_pid!=0 ) ) and role.status=1 and access.power_id=power.id and power.power_level=3 and power. power_pid={$moduleId} and power.status=1";
250 $rs = $db->query($sql);
251 foreach ($rs as $a){
252 $publicAction[$a['name']] = $a['id'];
253 }
254 unset($modules[$key]);
255 break;
256 }
257 }
258 // 依次读取模块的操作权限
259 foreach($modules as $key=>$module) {
260 $moduleId = $module['id'];
261 $moduleName = $module['name'];
262 $sql = "select power.id,power.name from ".
263 $table['role']." as role,".
264 $table['user']." as user,".
265 $table['access']." as access ,".
266 $table['power']." as power ".

249 "where user.user_id='{$authId}' and user.role_id=role.id and ( access.role_id=role.id or (access.role_id=role. role_pid and role.role_pid!=0 ) ) and role.status=1 and access.power_id=power.id and power.power_level=3 and power. power_pid={$moduleId} and power.status=1";
250 $rs = $db->query($sql);
251 foreach ($rs as $a){
252 $publicAction[$a['name']] = $a['id'];
253 }
254 unset($modules[$key]);
255 break;
256 }
257 }
258 // 依次读取模块的操作权限
259 foreach($modules as $key=>$module) {
260 $moduleId = $module['id'];
261 $moduleName = $module['name'];
262 $sql = "select power.id,power.name from ".
263 $table['role']." as role,".
264 $table['user']." as user,".
265 $table['access']." as access ,".
266 $table['power']." as power ".
267 "where user.user_id='{$authId}' and user.role_id=role.id and ( access.role_id=role.id or (access.role_id=role. role_pid and role.role_pid!=0 ) ) and role.status=1 and access.power_id=power.id and power.power_level=3 and power. power_pid={$moduleId} and power.status=1";
268 $rs = $db->query($sql);
269 $action = array();
270 foreach ($rs as $a){
271 $action[$a['name']] = $a['id'];
272 }
273 // 和公共模块的操作权限合并
274 $action += $publicAction;
275 $access[strtoupper($appName)][strtoupper($moduleName)] = array_change_key_case($action,CASE_UPPER);
276 }

277 }
278 return $access;
279 }
复制代码


 8、getModuleAccessList($authId,$module)//读取模块所属的记录访问权限

复制代码
static public function getModuleAccessList($authId,$module) {
283 // Db方式
284 $db = Db::getInstance(C('RBAC_DB_DSN'));
285 $table = array('role'=>C('RBAC_ROLE_TABLE'),'user'=>C('RBAC_USER_TABLE'),'access'=>C('RBAC_ACCESS_TABLE'));
286 $sql = "select access.power_id from ".
287 $table['role']." as role,".
288 $table['user']." as user,".
289 $table['access']." as access ".
290 "where user.user_id='{$authId}' and user.role_id=role.id and ( access.role_id=role.id or (access.role_id=role. role_pid and role.role_pid!=0 ) ) and role.status=1 and access.power_module='{$module}' and access.status=1";
291 $rs = $db->query($sql);
292 $access = array();
293 foreach ($rs as $power){
294 $access[] = $power['power_id'];
295 }
296 return $access;
297 }
复制代码
 
 
RBAC的委托认证方法
  1. authenticate($map,$model='User',$provider=USER_AUTH_PROVIDER)

代码方法是静态方法,支持三个参数,其中第一个认证条 件$map是必须的,可以灵活地控制需要认证的字段。 第二个参数是进行认证的模型类,默认是UserModel类 第三个参数是委托方式 由 USER_AUTH_PROVIDER 设置委托认证管理器的委托方式,目前支持的是 DaoAuthentictionProvider 通过数据库进行认证。 在应用系统的开发过程中,只需要设置相关的配置项和添加上面的认证方法,其他 的认证和决策访问就由RBAC组件的AccessDecision方法自动完成了。 系统会在执行某个模块的操作时候,首先判断该模块是否需要认证,如果 需要认证并且已经登录,就会获取当前用户的权限列表判断是否具有当前模块的当前操作权限,并进行相应的提示。 接下来就是在框架总后台设置相关项目的模块 和操作权限了。 关于如何授权请参考示例中心提供的RBAC示例
首先,在节点管理添加相关项目、模块和操作,作为权限管理的节点。  
如果需要设置公共的操作,可以使用Public模块,所有属于Public模块的操作对所有模块都有效。  
添加完成项目管理节点后,就在权限管理里面对某个用户组设置相关权限(包括项目权限、模块权限和操作权限)  
以后需要授权就把用户添加到某个权限组就可以了,同一个用户可以属于多个权限组。  
授权和认证功能涉及到四个数据表,DB_PREFIX为配置文件中设置的数据库前缀  
DB_PREFIX_group 权限组表  
DB_PREFIX_groupuser 组-用户关联表  
DB_PREFIX_access 访问权限表  
DB_PREFIX_node 权限节点表  
所谓的授权操作其实就是往DB_PREFIX_groupuser表和DB_PREFIX_access里面写入数据
至于数据表的字段官方给出的示例仅供参考,可以通过修改ORG.RBAC.AccessDecisionManager 类来完成项目的需要

posted on 2012-08-02 15:31  kudosharry  阅读(263)  评论(0编辑  收藏  举报

导航