>  기사  >  백엔드 개발  >  메뉴 표시줄 표시 및 자세한 권한 작업이 포함된 ThinkPHP의 RBAC 권한

메뉴 표시줄 표시 및 자세한 권한 작업이 포함된 ThinkPHP의 RBAC 권한

藏色散人
藏色散人앞으로
2019-04-16 10:13:564983검색

RBAC는 무엇이며 어떤 문제를 해결할 수 있나요?

RBAC는 역할 기반 액세스 제어(Role-Based Access Control)의 약어로, 중국어로 직역하면 사용자는 역할을 통해 권한과 연결됩니다. [해당 아키텍처는 운영 체제의 GBAC(GROUP)에서 영감을 받았습니다. - 기반 접근 제어]. 간단히 말해서 사용자는 여러 역할을 가질 수 있으며 각 역할에는 여러 권한이 있습니다. 이러한 방식으로 "사용자 역할 권한" 권한 부여 모델이 구성됩니다. 이 모델에서는 일반적으로 사용자와 역할, 역할과 권한 간에 다대다 관계가 있습니다. 해당 관계는 다음과 같습니다.

메뉴 표시줄 표시 및 자세한 권한 작업이 포함된 ThinkPHP의 RBAC 권한

많은 실제 응용 프로그램에서 시스템은 사용자에게 간단한 등록을 완료해야 할 뿐만 아니라 다양한 수준의 사용자가 다양한 리소스에 액세스하려면 다양한 운영 권한이 필요합니다. 그리고 기업 개발에서 권한 관리 시스템은 반복적인 개발을 위한 가장 효율적인 모듈 중 하나가 되었습니다. 여러 시스템에서 해당 권한 관리는 자체 시스템의 관리 요구 사항만 충족할 수 있습니다. 이는 데이터베이스 설계, 권한 액세스 및 권한 관리 메커니즘 측면에서 다를 수 있습니다. 이러한 불일치는 또한 다음과 같은 결과를 초래합니다.

• 여러 시스템 유지 관리 바퀴를 재발명하면 최첨단에 시간이 낭비됩니다
• 사용자 관리, 조직 메커니즘 등 데이터의 반복적인 유지 관리로 인해 데이터 무결성 및 일관성을 보장하기가 어렵습니다
• 권한 시스템 설계 및 개념 이해가 다르며 상응하는 기술적 차이가 있습니다. SSO(Single Sign-On)가 어렵고, 복잡한 기업 시스템도 어려움을 겪습니다. RBAC는 지속적인 실행을 기반으로 하는 비교적 성숙한 액세스 제어 솔루션입니다. 실제로 RBAC 모델을 기반으로 한 권한 관리 시스템을 사용하면 다음과 같은 이점이 있는 것으로 나타났습니다. 역할과 권한 간의 변경은 역할과 사용자 관계 간의 변경보다 상대적으로 느리기 때문에 권한 관리의 복잡성이 줄어들고 관리 오버헤드가 줄어듭니다. 그리고 애플리케이션 시스템의 보안 정책을 유연하게 지원할 수 있으며 애플리케이션 시스템의 변화에 ​​대한 확장성이 뛰어납니다. 권한 분배는 직관적이고 이해하기 쉬우며 계층적 권한은 계층적 사용자에게 적합합니다. 레벨 형태; 강한 섹스를 재사용합니다.

ThinkPHP의 RBAC 구현 시스템

ThinkPHP의 RBAC는 Java 기반 Spring의 Acegi 보안 시스템을 참조 프로토타입으로 사용하고 현재 ThinkPHP 구조에 적응하도록 단순화되었으며 다층의 사용자 정의 가능한 보안 시스템을 제공합니다. 애플리케이션 개발을 위한 보안 제어를 제공합니다. 보안 시스템은 주로 다음과 같은 부분으로 구성됩니다.

• 보안 인터셉터

• 인증 관리자

• 결정 액세스 관리자

• 실행 중인 Identity Manager

보안 인터셉터

보안 인터셉터는 도멘과 같습니다. 시스템의 보안 보호 시스템에 있는 다양한 보안 제어 링크는 특정 링크에서 보안 시스템 인증을 통과하지 못하면 보안 인터셉터가 이를 차단합니다.

인증 관리자

보호 시스템의 첫 번째 문은 인증 관리자입니다. 일반적으로 인증 관리자는 귀하의 주체(보통 사용자 이름)와 자격 증명(보통 비밀번호)을 확인합니다. ) 또는 자세한 내용을 확인하세요. 간단히 말하면, 인증 관리자는 귀하의 신원이 보안 보호 시스템의 인증 범위 내에 있는지 확인합니다.

접근결정관리

인증관리자의 본인확인을 통과했다고 해서 접근결정관리 문을 통과해야 하기 때문에 시스템에서 원하는 것을 모두 할 수 있는 것은 아닙니다. 액세스 결정 관리자는 사용자에게 권한을 부여하고 신원 인증 정보 및 보호되는 리소스와 관련된 보안 속성을 고려하여 시스템의 특정 모듈에 들어가 특정 작업을 수행할 수 있는지 여부를 결정합니다. 예를 들어, 보안 규칙에 감독자만 특정 모듈에 액세스할 수 있다고 명시되어 있고 귀하에게 감독자 권한이 부여되지 않은 경우 보안 인터셉터가 귀하의 액세스 작업을 가로챌 것입니다.

의사결정 접근 관리자는 단독으로 실행될 수 없으며 먼저 신원 확인을 위해 인증 관리자에 의존해야 합니다. 따라서 접근 결정 필터를 로드할 때 인증 관리자와 의사결정 접근 관리자가 이미 포함되어 있습니다.


애플리케이션의 다양한 요구 사항을 충족하기 위해 ThinkPHP는 액세스 결정 관리를 수행할 때 로그인 모드와 인스턴트 모드라는 두 가지 모드를 채택합니다. 로그인 모드에서는 사용자가 로그인할 때 시스템이 사용자의 인증 정보를 읽어 Session으로 변경하며, 다음 번에는 인증 정보를 다시 가져오지 않습니다. 즉, 관리자가 사용자의 권한을 수정하더라도 변경 사항은 사용자가 다음에 로그인한 후에만 적용됩니다. 인스턴트 모드는 위의 문제를 해결하기 위해 시스템의 모듈이나 작업에 접근할 때마다 사용자가 해당 모듈과 작업에 대한 권한을 가지고 있는지 즉시 확인하여 시스템의 보안을 더 높은 수준으로 보장합니다.

Identity Manager 실행

대부분의 응용 프로그램 시스템에서는 ID 관리자 실행의 유용성이 제한됩니다. 예를 들어 특정 작업이나 모듈에는 여러 ID의 보안 요구 사항이 필요합니다. ID 관리자를 실행하면 현재 ID를 다른 ID로 대체하여 보호된 객체에 더 깊이 액세스할 수 있습니다. 응용 프로그램 시스템 내에서. 이 보안 시스템 계층은 현재 RBAC에서 구현되지 않습니다.

ThinkPHP의 RBAC 인증 프로세스

위의 보안 시스템에 대응하여 ThinkPHP의 RBAC 인증 프로세스는 대략 다음과 같습니다.

1 현재 모듈의 현재 작동에 인증이 필요한지 여부를 확인합니다

2. 필수이며 로그인하지 않은 경우 인증 게이트웨이로 이동하여 이미 로그인되어 있는 경우 5

3. 위임된 인증을 통한 사용자 신원 인증

4. 사용자의 의사결정 액세스 목록을 얻습니다

5. 현재 사용자에게 접근 권한이 있는지 확인

권한 관리 프로세스의 구체적인 구현

RBAC 관련 데이터베이스 소개

ThinkPHP 전체 패키지에는 RBAC 처리 클래스 RBAC.class.php文件,位于Extend/Library/ORG/Util이 포함되어 있습니다. RBAC 사용에 필요한 4개의 테이블이 포함된 파일을 엽니다. SQL 문은 다음과 같습니다(복사 후 테이블 접두사를 교체하세요).

CREATE TABLE IF NOT EXISTS `think_access` (
  `role_id` smallint(6) unsigned NOT NULL,
  `node_id` smallint(6) unsigned NOT NULL,
  `level` tinyint(1) NOT NULL,
  `module` varchar(50) DEFAULT NULL,  KEY `groupId` (`role_id`),  KEY `nodeId` (`node_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;CREATE TABLE IF NOT EXISTS `think_node` (
  `id` smallint(6) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL,
  `title` varchar(50) DEFAULT NULL,
  `status` tinyint(1) DEFAULT '0',
  `remark` varchar(255) DEFAULT NULL,
  `sort` smallint(6) unsigned DEFAULT NULL,
  `pid` smallint(6) unsigned NOT NULL,
  `level` tinyint(1) unsigned NOT NULL,  PRIMARY KEY (`id`),  KEY `level` (`level`),  KEY `pid` (`pid`),  KEY `status` (`status`),  KEY `name` (`name`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8;CREATE TABLE IF NOT EXISTS `think_role` (
  `id` smallint(6) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL,
  `pid` smallint(6) DEFAULT NULL,
  `status` tinyint(1) unsigned DEFAULT NULL,
  `remark` varchar(255) DEFAULT NULL,  PRIMARY KEY (`id`),  KEY `pid` (`pid`),  KEY `status` (`status`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 ;CREATE TABLE IF NOT EXISTS `think_role_user` (
  `role_id` mediumint(9) unsigned DEFAULT NULL,
  `user_id` char(32) DEFAULT NULL,  KEY `group_id` (`role_id`),  KEY `user_id` (`user_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

다음은 RBAC와 관련된 데이터베이스 테이블 및 필드에 대한 소개입니다.

role_idSMALLINT(6)역할 IDly_accessrole_idSMALLINT(6)역할 IDnode_idSMALLINT(6) 노드 ID레벨 모듈

以下是数据库表各字段的关联关系:

메뉴 표시줄 표시 및 자세한 권한 작업이 포함된 ThinkPHP의 RBAC 권한

实现RBAC管理的前导性工作

基于ThinkPHP实现RBAC的权限管理系统中,首先要做一些前导性的工作(系统数据库设计TP已经为我们完成了),主要分以下几个方面:

• 用户(增、删、改、查)
• 角色(增、删、改、查)
• 节点(增、删、改、查)
• 配置权限(更新权限)

具体实现的代码如下(相关解释均在注释之中):

<?php /**
 *
 */
namespace Home\Controller;

use Home\Controller\BaseController;
use Home\Model\AdminUserModel;
use Org\Util\Tree;
use Think\Page;

class RbacController extends BaseController
{
    //初始化操作
    public function _initialize()
    {
        if (!IS_AJAX) $this->error('你访问的页面不存在,请稍后再试');
    }

    public function userIndex()
    {
        if (IS_POST) {
            $condition['username'] = array('like', "%" . trim(I('keybord')) . "%");
            $model = D('AdminUser');
            $count = $model->where($condition)->count();
            $Page = new Page($count, 3);
            $Page->setConfig('theme', '%FIRST% %UP_PAGE% %LINK_PAGE% %DOWN_PAGE% %END%');
            $show = $Page->show();
            //select search
            $list = $model->where($condition)->field('password', true)->relation(true)->limit($Page->firstRow . ',' . $Page->listRows)->select();
            $this->show = $show;
            $this->list = $list;
            $this->display('AdminUser/index');
        } else {
            $model = D('AdminUser');
            $count = $model->count();
            $Page = new Page($count, 6);
            $Page->setConfig('header', '共%TOTAL_ROW%条');
            $Page->setConfig('first', '首页');
            $Page->setConfig('last', '共%TOTAL_PAGE%页');
            $Page->setConfig('prev', '上一页');
            $Page->setConfig('next', '下一页');
            $Page->setConfig('link', 'indexpagenumb');
            $Page->setConfig('theme', '%FIRST% %UP_PAGE% %LINK_PAGE% %DOWN_PAGE% %END%');
            $show = $Page->show();
            //select search
            $list = $model->field('password', true)->relation(true)->limit($Page->firstRow . ',' . $Page->listRows)->select();
            $this->show = $show;
            $this->list = $list;
            $this->display('Rbac/userIndex');
        }

    }

    /*
    *  平台用户异步验证
    */
    public function checkUser()
    {
        $username = trim(I('username'));
        $conditions = array('username' => ':username');
        $result = M('AdminUser')->where($conditions)->bind(':username', $username)->find();
        //如果不存在,则可以创建,也就是返回的是true
        if (!$result) {
            echo 'true';
        } else {
            echo 'false';
        }
        exit();
    }

    /*
     *  创建平台用户,这里开启了服务器验证
     */
    public function createAdminUser()
    {
        if (IS_POST) {
            /**
             * [实例化User对象]
             * D方法实例化模型类的时候通常是实例化某个具体的模型类,如果你仅仅是对数据表进行基本的CURD操作的话,
             * 使用M方法实例化的话,由于不需要加载具体的模型类,所以性能会更高。
             */
            $model = D('AdminUser');
            /**
             * 如果创建失败 表示验证没有通过 输出错误提示信息
             * $model->create() 会自动调用验证规则
             */
            if (!$model->create()) return $this->error($model->getError());
            //$username = $model->username;
            //$model->add() 插入数据后会自动的摧毁数据
            if (!$uid = $model->add()) return $this->success('注册失败', U('Rbac/userIndex'));//获取用户id
            // 如果是注册用户的话
//            session('uid', $uid);
//            session('username', $username);
            //用户添加成功后,给用户角色表添加数据
            $role['role_id'] = I('role_id');
            $role['user_id'] = $uid;
            //添加该管理员操作到操作日志中
            $desc = '给ID为:[' . $_POST['role_id'] . ']的角色,新增用户:[' . $_POST['username'] . '],密码为:[' . $_POST['password'] . ']' . '其他参数' . $_POST;
            addOperationLog($desc);
            if (D('AdminRoleUser')->add($role)) {
                return $this->success('添加平台用户成功', U('Rbac/userIndex'));
            } else {
                return $this->error('添加平台用户失败', U('Rbac/userIndex'));
            }
            return $this->success('添加平台用户成功', U('Rbac/userIndex'));
        }
        $this->role_list = M('AdminRole')->select();
        $this->display('Rbac/createAdminUser');
    }

    /**
     * 改变用户角色
     * 可以支持不执行SQL而只是返回SQL语句:$User->fetchSql(true)->add($data);
     */
    public function updateUser()
    {
        $userId = I('get.id');
        if (IS_POST) {
            $data['user_id'] = I('post.user_id');
            $data['role_id'] = I('post.role_id');
            $model = M('AdminRoleUser');
            if ($model->where(array('user_id' => $data['user_id']))->delete() == false) {
                return $this->error('用户角色修改失败', U('Rbac/updateUser', array('id' => $userId)));
            }
            if ($model->add($data) == false) {
                return $this->error('用户角色修改失败', U('Rbac/updateUser', array('id' => $userId)));
            }
            return $this->success('用户角色修改成功', U('Rbac/userIndex'));
        }
        $this->role_list = M('AdminRole')->select();
        $this->user = M('AdminUser')->join('tour_admin_role_user ON tour_admin_role_user.user_id = tour_admin_user.id')->where(array('id' => $userId))->field('user_id,username,role_id')->find();
        $this->display();
    }

    //删除用户
    public function delUser()
    {
        $user_id = I('post.id', '', 'int');
        $user = D('AdminUser');
        $result = $user->relation(true)->where(array('id' => $user_id))->delete();
        if ($result) {
            //添加该管理员操作到操作日志中
            $desc = '删除用户ID:' . $user_id . '成功';
            addOperationLog($desc);
            $response = ['status' => 200, 'errmsg' => '删除成功', 'dataList' => $result];
            return $this->ajaxReturn($response, 'JSON');
        }
        //添加该管理员操作到操作日志中
        $desc = '删除用户ID:' . $user_id . '失败';
        addOperationLog($desc);
        $response = ['status' => 500, 'errmsg' => '删除失败', 'dataList' => $result];
        return $this->ajaxReturn($response, 'JSON');

    }

    //设置用户状态
    public function userStatus()
    {
        $uid = I('post.id');
        $db = M('AdminUser');
        $status = $db->where(array('id' => $uid))->getField('status');
        $status = ($status == 1) ? 0 : 1;
        if ($db->where(array('id' => $uid))->setField('status', $status)) {
            $response = ['status' => 200, 'errmsg' => '改变成功', 'dataList' => $status];
            return $this->ajaxReturn($response, 'JSON');
        }
        //添加该管理员操作到操作日志中
        $desc = '设置用户状态:' . $uid . '失败';
        addOperationLog($desc);
        $response = ['status' => 500, 'errmsg' => '改变失败', 'dataList' => $status];
        return $this->ajaxReturn($response, 'JSON');
    }

    /***********************************节点开始****************************************************/
    public function nodeIndex()
    {
        $db = M('AdminNode');
        $node = $db->order('id')->select();
        $this->nodelist = Tree::create($node);
        $this->display('Rbac/nodeIndex');
    }

    //创建权限表单处理
    public function createNode()
    {
        $db = M('AdminNode');

        //创建权限表单处理
        if (IS_POST) {
            $db->create();
            if (!$db->add()) {
                return $this->error("权限添加失败", U('Rbac/nodeIndex'));
            }
            return $this->success('权限添加成功', U('Rbac/nodeIndex'));
        }
        $node = $db->where('level !=3')->order('sort')->select();
        $this->nodelist = Tree::create($node);
        $this->display();
    }

    /*
    *   删除权限
    */
    public function delNode()
    {
        $result = M('AdminNode')->where(array('id' => I('post.id', '', 'int')))->delete();
        if ($result) {
            $response = ['status' => 200, 'errmsg' => '删除成功', 'dataList' => $result];
            return $this->ajaxReturn($response, 'JSON');
        }
        $response = ['status' => 500, 'errmsg' => '删除失败', 'dataList' => $result];
        return $this->ajaxReturn($response, 'JSON');
    }

    /*
    *   设置权限状态
    */
    public function NodeStatus()
    {
        $id = I('post.id');
        $db = M('AdminNode');
        $status = $db->where(array('id' => $id))->getField('status');

        $status = ($status == 1) ? 0 : 1;
        if ($db->where(array('id' => $id))->setField('status', $status)) {
            $response = ['status' => 200, 'errmsg' => '修改成功', 'dataList' => $status];
            return $this->ajaxReturn($response, 'JSON');
        }
        $response = ['status' => 500, 'errmsg' => '修改失败', 'dataList' => $status];
        return $this->ajaxReturn($response, 'JSON');
    }

    /*
   *   该节点是否在菜单栏显示
   */
    public function showMenus()
    {
        $id = I('post.id');
        $db = M('AdminNode');
        $show = $db->where(array('id' => $id))->getField('menus');
        $menus = ($show == 1) ? 0 : 1;
        $result = $db->where(array('id' => $id))->setField('menus', $menus);
        if ($result) {
            $response = ['status' => 200, 'errmsg' => '修改成功', 'dataList' => $result];
            return $this->ajaxReturn($response, 'JSON');
        }
        $response = ['status' => 500, 'errmsg' => '修改失败', 'dataList' => $result];
        return $this->ajaxReturn($response, 'JSON');
    }

    /***********************************角色开始****************************************************/
    public function roleIndex()
    {
        $db = M('AdminRole');
        $this->rolelist = $db->select();
        $this->display();
    }

    /*
     *创建角色
     */
    public function createAdminRole()
    {
        if (IS_POST) {
            $name = I('post.name', '', 'strip_tags');
            $remark = I('post.remark', '', 'strip_tags');
            $pid = I('post.pid', '', 'strip_tags');  // 用strip_tags过滤$_GET['title']
            if (empty($name)) return $this->error('角色名称不能为空');
            $role = M('AdminRole');
            $where['name'] = ':name';
            $roleName = $role->where($where)->bind(':name', $name, \PDO::PARAM_STR)->getField('name');
            if ($roleName) return $this->error("角色名称:'" . $name . "'已经存在", U('Rbac/roleIndex'));

            $role->name = $name;
            $role->remark = $remark;
            $role->pid = $pid;
            //create方法并不算是连贯操作,因为其返回值可能是布尔值,所以必须要进行严格判断。
            if ($role->create()) {
                // 如果主键是自动增长型 成功后返回值就是最新插入的值
                $result = $role->field('name,remark,pid')->add();
                //如果在add方法之前调用field方法,则表示只允许写入指定的字段数据,其他非法字段将会被过滤
                if (!$result) return $this->error("角色添加失败", U('Rbac/createpartent'));
                return $this->success('角色添加成功', U('Rbac/roleIndex'));
            }
            return $this->success('角色添加成功', U('Rbac/roleIndex'));
        }
        $this->display();
    }

    /*
     *添加权限Node位权限表,Access为权限-角色关联表
     */
    public function addNode()
    {
        $rid = I('rid', '', 'int');
        if (!is_numeric($rid)) return $this->success('参数类型错误,必须是数字', U('Rbac/roleIndex'));
        //getFieldById针对某个字段(ID)查询并返回某个字段(name)的值
        $roleModel = M('AdminRole');
        $roleWhere['id'] = ':id';
        $role_name = $roleModel->where($roleWhere)->bind(':id', $rid, \PDO::PARAM_INT)->getField('name');
        if ($role_name == false) return $this->success('没有找到该角色', U('Rbac/roleIndex'));

        //根据角色遍历所有权限
        $access = M('AdminAccess');
        if (IS_POST) {
            $actions = I('post.actions');
            try {
                $access->startTrans();
                $where['role_id'] = ':role_id';
                $mod1 = $access->where($where)->bind(':role_id', $rid)->delete();
                if (!$mod1) $access->rollback();
                $data = array();
                foreach ($actions as $value) {
                    $tmp = explode('_', $value);
                    $data[] = array(
                        'role_id' => $rid,
                        'node_id' => $tmp[0],
                        'level' => $tmp[1]
                    );
                }
                if (!($access->addAll($data))) {
                    $access->rollback();
                } else {
                    $access->commit();
                }
                return $this->success('权限设置成功', U('Rbac/addNode', array('rid' => $rid)));
            } catch (\Exception $e) {
                $access->rollback();
                return $this->success('权限设置异常', U('Rbac/addNode', array('rid' => $rid)));
            }

        }
        $node = M('AdminNode')->order('id')->select();
        $node_list = Tree::create($node);
        $node_arr = array();
        foreach ($node_list as $value) {
            $conditions['node_id'] = $value['id'];
            $conditions['role_id'] = $rid;
            $count = $access->where($conditions)->count();
            if ($count) {
                $value['access'] = '1';
            } else {
                $value['access'] = '0';
            }
            $node_arr[] = $value;
        }
        $this->role_name = $role_name;
        $this->node_list = $node_arr;
        $this->rid = $rid;
        $this->display();
    }

    /*
     *删除角色以及角色所拥有的权限
     */
    public function delRole()
    {
        $role_id = I('post.role_id', '', 'int');
        $user = D('AdminRole');
        $result = $user->relation(true)->where(array('id' => $role_id))->delete();
        if ($result) {
            $response = ['status' => 200, 'errmsg' => '修改成功', 'dataList' => $result];
            return $this->ajaxReturn($response, 'JSON');
        }
        $response = ['status' => 500, 'errmsg' => '修改失败', 'dataList' => $result];
        return $this->ajaxReturn($response, 'JSON');
    }

    /*
    *   设置角色状态
    */
    public function roleStatus()
    {
        $rid = I('post.rid');
        $db = M('AdminRole');
        $status = $db->where(array('id' => $rid))->getField('status');
        $status = ($status == 1) ? 0 : 1;
        if ($db->where(array('id' => $rid))->setField('status', $status)) {
            $response = ['status' => 200, 'errmsg' => '修改成功', 'dataList' => $status];
            return $this->ajaxReturn($response, 'JSON');
        }
        $response = ['status' => 500, 'errmsg' => '修改失败', 'dataList' => $status];
        return $this->ajaxReturn($response, 'JSON');
    }
}

ThinkPHP中RBAC类的详解

在ThinkPHP处理权限管理中,真正的难点并不是在RBAC类的使用上,上面相关的处理(权限配置,节点管理等);所以当完成以上工作,其实RBAC系统已经完成了90%。下面先从ThinkPHP中RBAC的配置说起(详细请参看对应的注释内容):

<?php  
return array(
 
"USER_AUTH_ON" => true, //是否开启权限验证(必配)
"USER_AUTH_TYPE" => 1, //验证方式(1、登录验证;2、实时验证)
 
"USER_AUTH_KEY" => 'uid', //用户认证识别号(必配)
"ADMIN_AUTH_KEY" => 'superadmin', //超级管理员识别号(必配)
"USER_AUTH_MODEL" => 'user', //验证用户表模型 ly_user
'USER_AUTH_GATEWAY' => '/Public/login', //用户认证失败,跳转URL
 
'AUTH_PWD_ENCODER'=>'md5', //默认密码加密方式
 
"RBAC_SUPERADMIN" => 'admin', //超级管理员名称
 
 
"NOT_AUTH_MODULE" => 'Index,Public', //无需认证的控制器
"NOT_AUTH_ACTION" => 'index', //无需认证的方法
 
'REQUIRE_AUTH_MODULE' => '', //默认需要认证的模块
'REQUIRE_AUTH_ACTION' => '', //默认需要认证的动作
 
'GUEST_AUTH_ON' => false, //是否开启游客授权访问
'GUEST_AUTH_ID' => 0, //游客标记
 
"RBAC_ROLE_TABLE" => 'ly_role', //角色表名称(必配)
"RBAC_USER_TABLE" => 'ly_role_user', //用户角色中间表名称(必配)
"RBAC_ACCESS_TABLE" => 'ly_access', //权限表名称(必配)
"RBAC_NODE_TABLE" => 'ly_node', //节点表名称(必配)
);

注意:

• 以上有的配置项并非必须的,但标有“必配”,则必须配置

• 无需认证的权限和方法与需要认证的模块和动作可以根据需要选择性配置,还有部分权限相关配置并未列表出

RBAC处理类提供静态的方法

ThinkPHP的RBAC处理类提供给我们很多相关的静态方法如下:

테이블 이름 필드 이름 필드 유형 Function
ly_user id INT 사용자 ID(고유 식별 번호)
username VARCHAR(16) Username
password VARCHAR(32) Password
email VARCHAR(100) 사용자 이메일
create_time TIMESTAMP 생성 시간(타임스탬프)
logintime TIMESTAMP 마지막 로그인 시간(타임스탬프)
loginip VARCHAR(15) 마지막 로그인 IP 주소
status TINYINT(1 ) 활성화 상태: 0: 비활성화됨을 의미합니다. ; 1: 활성화됨을 의미
remark VARCHAR(255) 설명 정보
ly_role id INT 역할 ID
이름 VARCHAR(20 ) 캐릭터 이름
pid SMALLINT(6) 부모 역할 해당 ID
status TINYINT(1) 활성화 상태(위와 동일)
remark VAR 챠(255 ) 비고정보
ly_node id SMALLINT(6) Node ID
name VARCHAR(20) 노드명 (영문명, 애플리케이션 컨트롤러에 해당, 애플리케이션, 메소드명)
title VARCHAR(50) 노드 중국어 이름(이해하기 쉬움)
status TINYINT(1) 활성화 상태(위와 동일)
remark VAR 문자( 255 ) 비고 정보
sort SMALLINT(6) 정렬 값(기본값은 50)
pid SMALLINT(6) 상위 노드 ID(예: 메소드 pid 해당 컨트롤러) )
level TINYINT(1) 노드 유형: 1: 애플리케이션(모듈)을 나타냅니다. 2: 컨트롤러를 나타냅니다.
方法名 接收参数说明 返回值 说明
RBAC::authenticate(map,map,model='');

$map:ThinkPHP数据库处理类的where条件参

数$model:用户表名,默认取配置文件C('USER_AUTH_MODEL')

array

RBAC::authenticate(array("username"=>"admin","userpwd" => I('POST.pwd','', 'md5')));

返回值是在用户表中,以$map为条件where的查阅结果集

0RBAC::saveAccessList($authId=null); $authId:用户识别号,默认取C('USER_AUTH_KEY'); 返回一个空值 如果验证方式为登录验证,则将权限写入session中,否则不作任何处理
RBAC::getRecordAccessList(authId=null,authId=null,module='');

$authId:用户识别号(可不传)

$module:当前操作的模块名称

Array 返回一个包含权限的ID的数组
RBAC::checkAccess() 返回true或false 检查当前操作是否需要认证(根据配置中需要认证和不需要评论的模块或方法得出)
RBAC::checkLogin() true 如果当前操作需要认证且用户没有登录,继续检测是否开启游客授权。如果开启游客授权,则写入游客权限;否则跳到登录页
RBAC::AccessDecision($appName=APP_NAME) $appName:选传,有默认值

true:表示有操作权限

false:无操作权限

AccessDecision(appName=APPNAME)方法,检测当前项目模块操作,是否存在于appName=APPNAME)方法,检测当前项目模块操作,是否存在于_SESSION['_ACCESS_LIST']数组中$_SESSION['_ACCESS_LIST']['当前操作']['当前模块']['当前操作']是否存在。如果存在表示有权限,返回true;否则返回flase。
RBAC::getAccessList($authId) $authId:用户识别号(选传,程序自动获取) Array 通过数据库查询取得当前认证号的所有权限列表
RBAC::getModuleAccessList(authId,authId,module) $authId:用户识别号(必)$module:对应的模块(必) Array 返回指定用户可访问的节点权限数组

注意:在使用RBAC::AccessDecision()方法时,如果你开启了项目分组,则必须传入当前分组,代码如下:

//权限验证
if(C(&#39;USER_AUTH_ON&#39;) && !$notAuth) {
import(&#39;ORG.Util.RBAC&#39;);
//使用了项目分组,则必须引入GROUP_NAME
RBAC::AccessDecision(GROUP_NAME) || $this->error("你没有对应的权限");
}

RBAC处理类的实际应用

在完成用户登录,角色创建,节点增删改查的工作后,就只剩下了RBAC如何在对应程序代码中应用了。挻简单的,只用在原来的代码其他上改动几个地方即可。

• 用户登录时,写入用户权限
• 用户操作时,进行权限验证

下面是用户登录时的实现代码:

<?php namespace Home\Controller;

use Think\Controller;
use Org\Util\Rbac;

class LoginController extends Controller
{
    public function index()
    {
        $this->display();
    }

    /*
     * 异步验证账号
    */
    public function checkUser()
    {
        $username = I('username');
        $conditions = array('username' => ':username');
        $result = M('User')->where($conditions)->bind(':username', $username)->find();
        //如果不存在,则可以创建,也就是返回的是true
        if (!$result) {
            echo 'false';
        } else {
            echo 'true';
        }
        exit();
    }

    /*
     * 异步验证密码
    */
    public function checkPwd()
    {
        $username = I('post.username');
        $password = I('post.password');
        $conditions = array('username' => ':username');
        $result = M('User')->where($conditions)->bind(':username', $username)->find();
        if (md5($password) != $result['password']) {
            echo 'false';
        } else {
            echo 'true';
        }
        exit();
    }

    /*
     * 检查登录
    */
    public function checkLogin()
    {
        if (!IS_POST) $this->error('非法访问');

        // 采用htmlspecialchars方法对$_GET['name'] 进行过滤,如果不存在则返回空字符串
        $username = I('post.username', '', 'htmlspecialchars');
        // 采用正则表达式进行变量过滤,如果正则匹配不通过的话,则返回默认值。
        //I('get.name','','/^[A-Za-z]+$/');
        $password = md5(I('post.password'));

        $user = D('AdminUser');
        $where = array('username' => $username);
        $fields = array('id', 'password', 'username', 'status', 'expire', 'logintime'); // 之查找需要的字段
        $result = $user->where($where)->field($fields)->find();

        if (!$result || $password != $result['password']) return $this->error('账号或密码错误',U('Home/Login/index'));

        if ($result['status'] == 0) return $this->error('该用户被锁定,暂时不可登录',U('Home/Login/index'));

        // 是否记住我的登录,设置一个Cookie,写在客户端
        if (isset($_POST['remember'])) {
            $value = $result['id'] . '|' . get_client_ip() . '|' . $result['username'];
            $value = encrytion($value, 1);
            @setcookie('remember', $value, C('AUTO_LOGIN_LIFETIME'), '/');
        }

        // 每天登录增加经验值
        $today = strtotime(date('Y-m-d')); // 获取今天0时0分0秒的时间
        // 如果上次的登录时间小于今天的时间,则增加经验值
        $where2 = array('id' => $result['id']);
        if ($result['logintime'] where($where2)->setInc('expire', 10);
        }

        //更新登录户登录信息
        $data_arr = array(
            'id' => $result['id'],
            'logintime' => time(),
            'loginip' => get_client_ip(),
        );
        if ($user->save($data_arr)) {
            // 获取$_SESSION['user_id'] 如果不存在则默认为0
            session('uid', 0);
            session('username', $result['username']);
            session('loginAccount', $result['username']);
            session('loginUserName', $result['username']);
            session('uid', $result['id']);
            //RBAC 开始,用户认证SESSION标记 ,默认为"authId"
            session(C('USER_AUTH_KEY'), $result['id']);

            //如果为超级管理员,则无需验证
            if ($_SESSION['username'] == C('RBAC_SUPERADMIN')) session(C('ADMIN_AUTH_KEY'), true);

            //用于检测用户权限的方法,并保存到Session中,读取用户权限
            Rbac::saveAccessList($result['id']);
            //添加操作日志中
            $desc = '登陆成功';
            addOperationLog($desc);
            return $this->redirect('Index/index');
        } else {
            return $this->error('2222222222222');
        }

    }

    public function memberInfo()
    {
        $user_id = session('user_id');
        $user = D('User');
        $where = array('user_id' => $user_id);
        $result = $user->where($where)->select();
        $this->result = $result;
        $this->display();
    }

    /**
     * 获取apikey_values
     */

    public function apikey()
    {
        $secret = "6JNVkTk4jHsgF0e1oOVLwOZDeq83pDXa";
        $user_id = I('user_id', '', int);
        // $where = array('user_id = %d ',array($user_id));
        $user = M('User');
        // $result = $user->where($where)->find();
        $result = $user->where("user_id = %d", array($user_id))->find();

        $hash = sha1($result['user_id'] . $result['password'] . $secret);
        $data = array(
            'apikey_value' => $hash,
            'apikey_time' => time()
        );
        $where = array('user_id' => $user_id);
        $user->where($where)->save($data);
        $this->ajaxReturn($hash);
    }

    public function hash()
    {
        if (!IS_POST) {
            $out_data = array(
                'err_msg' => 'request method is error.',
                'is_success' => 'Fail'
            );
            exit(json_encode($out_data));
        };

        $username = I('username');
        $password = I('password');
        $where = array('username' => $username);

        $user = M('User');
        $result = $user->where($where)->find();
        if (!$result || $password != $result['password']) {
            $out_data = array(
                'err_msg' => 'Username or password is incorrect.',
                'is_success' => 'Fail'
            );
            exit(json_encode($out_data));
        }

        /**
         * HASH生成规则
         */
        $secret = "6JNVkTk4jHsgF0e1oOVLwOZDeq83pDXa";
        $hash = sha1($result['user_id'] . $result['password'] . $secret);

        $where = array('user_id' => $result['user_id']);
        $data["apikey_value"] = $hash;
        $data["apikey_time"] = time();
        $user->where($where)->save($data);

        $out_data = array(
            'is_success' => 'Success',
            'hash' => $hash
        );
        exit(json_encode($out_data));
    }

    public function relationTest()
    {
        echo get_client_ip();
    }

    public function error1(){
        $this->display();
    }

    public function logout()
    {
        session('username', NULL);
        session_unset();
        session_destroy();
        return $this->redirect('Login/index');
    }
}

接着在控制器目录创建一个CommonAction.class.php文件,然后改写所有要权限验证的类,让其继承自CommonAction。代码如下:

<?php namespace Home\Controller;

use Think\Controller;
use Org\Util\Rbac;
class BaseController extends Controller
{
//    /**
//     * ThikPHP自动运行方法,每一次,都会自动运行这个方法
//     * 要判断一个session值是否已经设置,可以使用
//       session(&#39;?name&#39;); 相当于:isset($_SESSION[&#39;name&#39;]);
//     */
    public function _initialize(){
        /***************************************网站开关****************************************************/
        if(!C(&#39;WEB_STATE&#39;)) exit(&#39;网站维护中&#39;);

        /***********************************没有登录时候时需要执行的代码**************************************/
        if(!isset($_SESSION[C(&#39;USER_AUTH_KEY&#39;)])) return $this->redirect('Login/index');

        /***************************************自动登录配置************************************************/
        if(isset($_COOKIE['remember']) && !isset($_SESSION['uid'])){
            // Cookie 解密
            $value = encrytion($_COOKIE['remember']);
            $result = explode('|',$value);
            // 判断IP地址是否一样
            if($result[1] == get_client_ip()){
                session('uid',$result[0]);
                session('uid',$result[2]);
            }
        }

        /***************************************权限认证****************************************************/
        $Public = in_array(MODULE_NAME,explode(',',C('NOT_AUTH_MODULE'))) || in_array(ACTION_NAME,explode(',',C('NOT_AUTH_ACTION')));
        // 如果不在公共模块之中,同时开启权限验证的话,则开始认证过程
        if(C('USER_AUTH_ON') && !$Public)
        {
            if(!Rbac::AccessDecision()) //通过accessDecision获取权限信息,true:表示有操作权限,false:无操作权限
            {
                return $this->error("你没有对应的权限");  //没有获取到权限信息时需要执行的代码
            }
        }
        /***************************************导航栏菜单显示****************************************************/
        /*
        *   思路:
        *   1.取出所有权限节点。
        *   2.取出当前登录用户拥有的模块权限(取英文名称)和操作权限(取ID)
        *   3.对所有权限进行遍历,先匹配模块权限,不存在删除,存在则匹配操作权限
        */
        // 超级管理员登录
        if(session(C('ADMIN_AUTH_KEY')))
        {
            $menus = D('AdminNode')->where('level = 2')->relation(true)->order('sort desc')->select();//取出所有节点
        }else{
            /**
             * [1]取出所有的权限,是通过关联模型从数据库中获取的
             */
            $menus = D('AdminNode')->where('level = 2')->relation(true)->order('sort desc')->select();
            $module = '';    //存放拥有的模块
            $node_id = '';   //存放拥有的模块
            /**
             * [2]获取当前用户的所有权限,这个是从RBAC中获取的
             */
            $access_list = $_SESSION['_ACCESS_LIST'];   //当前用户所拥有的权限
            foreach ($access_list as $key => $value) {
                foreach ($value as $key1 => $value1) {
                    $module = $module.','.$key1;    //字符串拼接模块名称
                    foreach ($value1 as $key2 => $value2) {
                        $node_id = $node_id.','.$value2;    //字符串拼操作id
                    }
                }
            }
            /**
             * [3]去除没有权限的节点,通过所有权限和用户已经拥有的权限比较
             */
            foreach ($menus as $key => $value) {
                $all_node[] = $value['name'];
                if(!in_array(strtoupper($value['name']), explode(',', $module))){
                    unset($menus[$key]);    //删除模块
                }else{
                    //模块存在,比较里面的操作
                    foreach ($value['node'] as $key1 => $value1) {
                        if(!in_array($value1['id'], explode(',', $node_id))){
                            unset($menus[$key]['node'][$key1]);  // 删除操作
                        }
                    }
                }
            }
        }
        $this->menus = $menus;
    }

}

ThinkPHP中提供了一个_initialize()方法,是在类初始化就会执行的,也就是只要后面控制器继承自CommonAction类,就会在作对应操作时,执行_initialize()方法。

위 내용은 메뉴 표시줄 표시 및 자세한 권한 작업이 포함된 ThinkPHP의 RBAC 권한의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 cnblogs.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제
이전 기사:출현 모드의 역할다음 기사:출현 모드의 역할