thinkcmf 角色授权支持分类
ThinkCMF中的权限是以后台菜单为基础来进行设置的(menu table),即如果你需要一个自定义的权限,那么你需要在后台菜单里添加一项菜单,然后在角色管理里可以针对角色进行授权
而现在遇到一个需求:管理员可以自行选择哪些用户可以查看某些分类(除了几个指定分类外其他分类都是公开的)
因为我这里的用户其实就是相当于不同的管理员,所以我就直接把权限问题整合到了现有的“角色授权”功能里
因为(只有几个分类需要授权,所以我将这些分类都移到一个一级分类里"内网登录"
然后在后台角色授权(RbacController)里直接对该分类及子分类进行处理(该分类ID保存在配置文件中AUTHORIZE_CATID)
\application\Admin\Controller\RbacController.class.php 中添加了对分类进行授权的功能
/** * 角色授权 */ public function authorize() { $this->auth_access_model = D("Common/AuthAccess"); //角色ID $roleid = intval(I("get.id")); if (!$roleid) { $this->error("参数错误!"); } import("Tree"); $menu = new \Tree(); $menu->icon = array('│ ', '├─ ', '└─ '); $menu->nbsp = ' '; $result = $this->initMenu(); $newmenus=array(); $priv_data=$this->auth_access_model->where(array("role_id"=>$roleid))->getField("rule_name",true);//获取权限表数据 foreach ($result as $m){ $newmenus[$m['id']]=$m; } foreach ($result as $n => $t) { $result[$n]['checked'] = ($this->_is_checked($t, $roleid, $priv_data)) ? ' checked' : ''; $result[$n]['level'] = $this->_get_level($t['id'], $newmenus); $result[$n]['parentid_node'] = ($t['parentid']) ? ' class="child-of-node-' . $t['parentid'] . '"' : ''; } $str = "<tr id='node-\$id' \$parentid_node> <td style='padding-left:30px;'>\$spacer<input type='checkbox' name='menuid[]' value='\$id' level='\$level' \$checked onclick='javascript:checknode(this);'> \$name</td> </tr>"; $menu->init($result); $categorys = $menu->get_tree(0, $str); //需要授权的分类列表 $categorys.= $this->_authorize_cat($priv_data);
$this->assign("categorys", $categorys); $this->assign("roleid", $roleid); $this->display(); } /** * 角色授权 */ public function authorize_post() { $this->auth_access_model = D("Common/AuthAccess"); if (IS_POST) { $roleid = intval(I("post.roleid")); if(!$roleid){ $this->error("需要授权的角色不存在!"); } if (is_array($_POST['menuid']) && count($_POST['menuid'])>0) { $menu_model=M("Menu"); $auth_rule_model=M("AuthRule"); $this->_authorize_post_cat($_POST['catid'],$roleid);
$this->auth_access_model->where(array("role_id"=>$roleid,'type'=>'admin_url'))->delete(); foreach ($_POST['menuid'] as $menuid) { $menu=$menu_model->where(array("id"=>$menuid))->field("app,model,action")->find(); if($menu){ $app=$menu['app']; $model=$menu['model']; $action=$menu['action']; $name=strtolower("$app/$model/$action"); $this->auth_access_model->add(array("role_id"=>$roleid,"rule_name"=>$name,'type'=>'admin_url')); } } $this->success("授权成功!", U("Rbac/index")); }else{ //当没有数据时,清除当前角色授权 $this->auth_access_model->where(array("role_id" => $roleid))->delete(); $this->error("没有接收到数据,执行清除授权成功!"); } } } /** * 角色授权-支持指定分类 * @Author HTL * @DateTime 2016-08-10T10:20:01+0800 * @param string $priv_data[auth_access table data] * @return [type] [description] */ function _authorize_cat($priv_data) { $cat_id = intval(C("AUTHORIZE_CATID"));//from config.php ,该分类必须是一级分类 if($cat_id<=0) return; $cats = M("Terms")->where("status=1 and path like '%0-$cat_id%'")->order("parent,listorder,term_id")->getField("term_id as id,name,parent,path",true); if(!cats || count($cats)<=0) return; $cat_html = "";//生成的html //根据不同level生成不同层级效果 $level_spacer = array('',' ├─' ,' │ ├─ ',' │ │ └─ ' ); $str = "<tr id='cat-#id#' #parentid_node#> <td style='padding-left:30px;'>#spacer#<input type='checkbox' name='catid[]' value='#id#' level='#level#' #checked# onclick='javascript:checknode(this);'> #name#</td> </tr>"; foreach ($cats as $key => $value) { $checked = in_array($value['id'],$priv_data)?"checked":""; //权限中是否已经存在该分类id $level = count(explode('-',$value['path']))-2; //减2是因为:要去掉数据库中的0-不算和explode方法是从1开始的 $parentid_node = ($value['parent']) ? ' class="child-of-cat-' . $value['parent'] . '"' : '';//父类 $_str = str_replace('#id#',$value['id'],$str); $_str = str_replace("#spacer#",$level_spacer[$level],$_str); $_str = str_replace("#level#",$level,$_str); $_str = str_replace("#checked#",$checked,$_str); $_str = str_replace("#parentid_node#",$parentid_node,$_str); $_str = str_replace("#name#",$value['name'],$_str); $cat_html .= $_str; } return '<tr><td><br></td></tr>'.$cat_html; } /** * 将选择的分类保存到权限表中 * @Author HTL * @DateTime 2016-08-10T11:45:48+0800 * @param [type] $catids [post catids] * @param [type] $roleid [current user roleid] * @return [type] [description] */ function _authorize_post_cat($catids,$roleid) { if(!$catids || count($catids)<=0 || $roleid<=0) return; //使用单独的type(cat_url)进行区分 $this->auth_access_model->where(array("role_id"=>$roleid,'type'=>'cat_url'))->delete(); foreach ($catids as $catid) { $this->auth_access_model->add(array("role_id"=>$roleid,"rule_name"=>$catid,'type'=>'cat_url')); } }
\data\conf\config.php 中定义一个配置项,并指定一级分类ID
<?php return array ( 'AUTHORIZE_CATID' => 30,/*需要授权才能访问的一级分类ID(自动包括所有子分类),必须是一级分类*/ );?>
修改后的角色授权页面
保存后的数据库信息(cmf_auth_access)
2016-08-11 update:
之前没有考虑一个问题是:在添加或编辑文章时如果管理员对文章同时选择了有权限的分类和没有权限的分类,那这种情况权限不就没有用了吗?
下面是我的解决办法,即在保存文章时去判断是否同时选择了有权限和无权限的分类,如果有则不能进行保存操作,
\application\Portal\Controller\AdminPostController.class.php
/** * 在显示模板之前调用此方法 $this->get_authority_cats();,用于前端处理 * 获取需要权限的所有分类 * 用于防止管理员同时选择有权限的分类和没有权限的分类 * @AuthorHTL * @DateTime 2016-08-11T11:01:49+0800 * @param boolean $is_view [是否在页面上展示,default=true] * @return [type] [description] */ function get_authority_cats($is_view = true) { $cat_id = intval(C("AUTHORIZE_CATID"));//需要权限的一级 if($cat_id<=0) return; $cats = M("Terms")->where("status=1 and path like '%0-$cat_id%'")->order("parent,listorder,term_id")->getField("term_id",true); if($is_view){ $this->assign("authrize_cats",implode(",",$cats)); } else{ return $cats; } } /** * 在保存文章之前调用此方法 $this->check_cats($_POST['term']),防止前端检测未执行或被人为绕过 * 检查文章的分类是否同时包含了有权限的和无权限的 * 要么选择的全部是有权限的 * 要么选择的全部是无权限的 * 不能混合选择 * @AuthorHTL * @DateTime 2016-08-11T11:09:07+0800 * @param [type] $_POST [description] * @return [type] [description] */ function check_cats($post_cats) { if(!$post_cats || count($post_cats)<=0) return; $first_result = ture; $auth_cats = $this->get_authority_cats(false); if(!$auth_cats || count($auth_cats)<=0) return; foreach ($post_cats as $i=>$mterm_id){ //以首行的结果做比较基础 if($i<=0){ $first_result = false;//in_array($mterm_id, $auth_cats); } //如果跟首次的不同则说明包含有权限和没有权限的分类 else if($first_result != in_array($mterm_id,$auth_cats)){ $this->error("不能同时选择有权限的栏目和无权限的栏目!"); return false; } } }
前端页面的脚本处理
/** * 检查文章的分类是否同时包含了有权限的和无权限的 * 要么选择的全部是有权限的 * 要么选择的全部是无权限的 * 不能混合选择 * @AuthorHTL * @DateTime 2016-08-11T10:41:05+0800 * @return {[type]} [description] */ function check_cat() { var _term_vals = $("#term").val()//选择的栏目列表 ,first_result = true//default value ,auth_cats = ',{$authrize_cats},';//有权限的栏目 //只有选择了一个分类或没有权限分类 if(_term_vals.length<=1 || auth_cats.length<=2) return true; for(i in _term_vals){ //以首行的结果做比较基础 if(i<=0){ first_result = auth_cats.indexOf(","+_term_vals[i]+",")>=0; } //如果跟首次的不同则说明包含有权限和没有权限的分类 else if(first_result !== auth_cats.indexOf(","+_term_vals[i]+",")>=0){ alert("不能同时选择有权限的栏目和无权限的栏目!"); return false; } } return true; } //提交前先检测 $(".J_ajax_submit_btn").click(function () { return check_cat(); });