4

之前有个朋友问我yii的rbac怎么做,以前大学的时候有接触过,很长时间没用了,也忘记了,正好这几天比较闲,重新捋了下大体思路,希望可以帮到困在yii的rbac上的门外han~~

RBAC~~什么是RBAC

基于角色的权限访问控制(Role-Based Access
Control)作为传统访问控制(自主访问,强制访问)的有前景的代替受到广泛的关注。在RBAC中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。在一个组织中,角色是为了完成各种工作而创造,用户则依据它的责任和资格来被指派相应的角色,用户可以很容易地从一个角色被指派到另一个角色。角色可依新的需求和系统的合并而赋予新的权限,而权限也可根据需要而从某角色中回收。角色与角色的关系可以建立起来以囊括更广泛的客观情况。

RBAC~~权限认证流程

  1. 用户登录后认证用户的角色

  2. 根据角色 查询出该角色拥有的权限操作列表

  3. 访问某一个权限(操作)时判断该用户是否拥有访问的能力

yii如何实现呢??这里我们采用数据库的方式实现,会相对来说比较安全

RBAC~~权限数据生成

首先在web/console.php中配置组件

'authManager' => [
    'class' => 'yii\rbac\DbManager',
    // auth_item (role permission)
    // auth_item_child (role->permission)
    // auth_assignment (user->role)
    // auth_rule (rule)
    'itemTable' => '{{%auth_item}}',
    'itemChildTable' => '{{%auth_item_child}}',
    'assignmentTable' => '{{%auth_assignment}}',
    'ruleTable' => '{{%auth_rule}}',
],

接着使用yii脚本,生成数据表

./yii migrate --migrationPath=@yii/rbac/migrations/    

然后我们需要读取所有的控制器与方法 存储到权限表,方便判断的时候 读取数据库 进而进行判断

public function actionInit()
{
    $trans = Yii::$app->db->beginTransaction();
    try {
        //构建控制器目录
        $dir = dirname(dirname(__FILE__)). '/modules/controllers';
        //找到控制器目录下的所有文件
        $controllers = glob($dir. '/*');
        $permissions = [];
        foreach ($controllers as $controller) {
            $content = file_get_contents($controller);
            //找到Controller即可
            preg_match('/class ([a-zA-Z]+)Controller/', $content, $match);
            $cName = $match[1];
            $permissions[] = strtolower($cName. '/*');
            //正则匹配文本中的所以action
            preg_match_all('/public function action([a-zA-Z_]+)/', $content, $matches);
            foreach ($matches[1] as $aName) {
                $permissions[] = strtolower($cName. '/'. $aName);
            }
        }
        $auth = Yii::$app->authManager;

        //为什么$auth可以操作到该表
        foreach ($permissions as $permission) {
            //是否存在该权限
            if (!$auth->getPermission($permission)) {
                $obj = $auth->createPermission($permission);
                $obj->description = $permission;
                $auth->add($obj);
            }
        }
        $trans->commit();
        echo "import success \n";
    } catch(\Exception $e) {
        $trans->rollback();
        echo "import failed \n";
    }
}

接着yii下 就可以生成权限数据了

./yii rbac/init

然后,看看数据库

clipboard.png

RBAC~~权限访问控制

过滤器的使用原理:

权限的控制实则就是判断当前的用户是否拥有对方法,控制器访问的权限 在这之前需要使用过滤器 对用户的登录与未登录进行认证过滤 ,所有的控制器 extends CommonController,在访问子类控制器的时候,会自动去访问behaviors方法,在子控制器中,通过重写父类的属性指定允许访问的方法,进而实现过滤的作用。

 //子类可以通过重写该属性 实现认证
 public $mustlogin = ['createrule', 'createrole', 'roles', 'assignitem'];
 //行为过滤器
 public function behaviors()
    {
        return [
            'access' => [
                'class' => \yii\filters\AccessControl::className(),
                'user' => 'admin',
                'only' => $this->actions,
                'except' => $this->except,
                'rules' => [
                    [
                        'allow' => false,
                        'actions' => empty($this->mustlogin) ? [] : $this->mustlogin,
                        'roles' => ['?'],
                    ],
                    [
                        'allow' => true,
                        'actions' => empty($this->mustlogin) ? [] : $this->mustlogin,
                        'roles' => ['@'],
                    ],
                ],
            ],
        ];
    }

//那么如何实现访问 判断用户是否拥有权限呢??yii提供了beforeAction,在CommonController进行判断

     /*权限访问判断*/
    public function beforeAction($action)
    {
         //调用父类方法   防止被重写掉
        if (!parent::beforeAction($action)) {
            return false;
        }
        //获取到当前的控制器
        $controller = $action->controller->id;
        $actionName = $action->id;
        if (Yii::$app->admin->can($controller. '/*')) {
            return true;
        }
        if (Yii::$app->admin->can($controller. '/'. $actionName)) {
            return true;
        }
        throw new \yii\web\UnauthorizedHttpException('对不起,您没有访问'. $controller. '/'. $actionName. '的权限');
        // return true;
    }

//这样即可实现权限的访问控制   

到这里,我们就是实现了权限的访问判断,接下来 我们还要实现 不同用户显示不同的菜单,如何实现呢??

原理:
将菜单栏以数组的形式进行存储,通过循环数组,判断当前用户访问的控制器是否拥有对应的权限

 <?php
                $controller = Yii::$app->controller->id;
                $action = Yii::$app->controller->action->id;
                //循环菜单
                foreach (Yii::$app->params['adminmenu'] as $menu) {
                    $show = "hidden";
                    if (Yii::$app->admin->can($menu['module']. '/*')) {
                        $show = "show";
                    } else {
                        if (empty($menu['submenu']) && !Yii::$app->admin->can($menu['url'])) {
                            continue;
                        } else {
                            foreach ($menu['submenu'] as $sub) {
                               //判断当前的用户是否拥有访问该控制器的权限
                                if (Yii::$app->admin->can($menu['module']. '/'. $sub['url'])) {
                                    $show = "show";
                                }
                            }
                        }
                    }
                ?>
              <li class="<?php echo $controller == $menu['module'] ? 'active' : ''; echo $show; ?>">
                <a <?php echo !empty($menu['submenu']) ? 'class="dropdown-toggle"' : ''; ?> href="<?php echo $menu['url'] == '#' ? '#' : yii\helpers\Url::to([$menu['url']]); ?>">
                    <i class="<?php echo $menu['icon'] ?>"></i>
                    <span><?php echo $menu['label']; ?></span>
                    <?php if (!empty($menu['submenu'])) : ?>
                    <i class="icon-chevron-down"></i>
                    <?php endif; ?>
                </a>
                    <ul class="submenu <?php echo $controller == $menu['module'] && !empty($menu['submenu']) ? 'active' : ''; ?>">
                    <?php foreach ($menu['submenu'] as $sub): ?>
                    <?php if (!Yii::$app->admin->can($menu['module']. '/*') && !Yii::$app->admin->can($menu['module']. '/'. $sub['url'])) continue; ?>
                    <li><a href="<?php echo yii\helpers\Url::to([$menu['module']. '/'. $sub['url']]); ?>"><?php echo $sub['label'] ?></a></li>
                    <?php endforeach; ?>
                </ul>
            </li>
            <?php
                }
            ?> 

这样就实现了不同登录用户 不同菜单的展示

RBAC~~创建不同角色

原理:
不同角色拥有不同权限,超级管理员可以创建用户,及分配权限给不同用户,首先我们得有个用户->

 /*角色添加*/
    public function actionCreaterole()
    {
        if (Yii::$app->request->isPost) {
            //DBmanager对象
            $auth = Yii::$app->authManager;
            //创建一个role的对象
            $role = $auth->createRole(null);
            $post = Yii::$app->request->post();
            if (empty($post['name']) || empty($post['description'])) {
                throw new \Exception('参数错误');
            }
            $role->name = $post['name'];
            $role->description = $post['description'];
            $role->ruleName = empty($post['rule_name']) ? null : $post['rule_name'];
            $role->data = empty($post['data']) ? null : $post['data'];
            if ($auth->add($role)) {
                Yii::$app->session->setFlash('info', '添加成功');
            }
        }
        return $this->render('_createitem');
    }

原理:
所谓的分配权限就是将给用户分配是否可以访问控制器的权限,确认要分配的对象 实则是指定表的user_id对应的item_name值

clipboard.png

具体如何做呢?? 看这里->

 /* 分配权限 */
    public function actionAssignitem($name)
    {
        //获取到角色
        $name = htmlspecialchars($name);
        $auth = Yii::$app->authManager;
        //获取到当前角色的信息
        $parent = $auth->getRole($name);
        if (Yii::$app->request->isPost) {
            $post = Yii::$app->request->post();
            if (Rbac::addChild($post['children'], $name)) {
                Yii::$app->session->setFlash('info', '分配成功');
            }
        }
        $children = Rbac::getChildrenByName($name);
        //获取当前用户的
        $roles = Rbac::getOptions($auth->getRoles(),$parent);
        //获取当前用户拥有的权限
        $permissions = Rbac::getOptions($auth->getPermissions(), $parent);
        return $this->render('_assignitem', ['parent' => $name, 'roles' => $roles, 'permissions' => $permissions, 'children' => $children]);
    }

RBAC~~规则指定,更高级的权限指定

接下来,不得不谈谈rbac的规则,这又是个什么东西呢?? 所谓的规则 其实就是对用户的权限的额外限制
原理:
通过定义规则类,添加用户的时候指定规则名称 就可以实现对用户的权限的额外限制,
数据表中,data字段就是实例化的自定义规则类对象

比如我们定义这样一个自定义规则类

/*实现分类只能由添加者删除*/
class AuthorRule extends Rule
{
    public $name = "isAuthor";
    //当前的用户,权限,额外的参数
    public function execute($user, $item, $params)
    {
        $action = Yii::$app->controller->action->id;
        //对delete方法 进行额外限制
        if ($action == 'delete') {
            //获取到当前分类是由那个用户添加的  不允许其他用户删除
            $cateid = Yii::$app->request->get("id");
            $cate = Category::findOne($cateid);
            return $cate->adminid == $user;
        }
        return true;
    }
}

我们如何创建这个规则到数据表中呢??

/*创建规则*/
    public function actionCreaterule()
    {
        if (Yii::$app->request->isPost) {
            $post = Yii::$app->request->post();
            if (empty($post['class_name'])) {
                throw new \Exception('参数错误');
            }
            //指定当前的规则命名空间
            $className = "app\\models\\". $post['class_name'];
            if (!class_exists($className)) {
                throw new \Exception('规则类不存在');
            }
            $rule = new $className;
            //使用authManager组件将当前的规则类 保存到数据库
            if (Yii::$app->authManager->add($rule)) {
                Yii::$app->session->setFlash('info', '添加成功');
            }
        }
        return $this->render("_createrule");
    }
    

这样再添加用户的时候,就可以指定规则,进而实现对更加高级的指定权限

到这里,基本完成了用户创建,权限分配,及规则指定,希望可以帮到大家~~


萧逸
709 声望29 粉丝

致力于分享效率工具、有趣好玩的开源项目、技术干货。关注我,带你发现新大陆~