ThinkPHP-RBAC
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’])
login
index
logout
checkLogin
changePwd
profile
verify
change
二、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方法有导入这个文件。
{
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='') {
105 if(empty($module)) $module = MODULE_NAME;
106 //获取权限访问列表
107 $accessList = RBAC::getModuleAccessList($authId,$module);
108 return $accessList;
109 }
4、checkAccess() 方法 检测当前模块和操作是否需要验证 返回bool类型
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() {
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。
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']的值,取得当前认证号的所有权限列表。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
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 类来完成项目的需要