Rbac.class.php 12.7 KB
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2009 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace Org\Util;

use Think\Db;

/**
 * +------------------------------------------------------------------------------
 * 基于角色的数据库方式验证类
 * +------------------------------------------------------------------------------
 */
// 配置文件增加设置
// USER_AUTH_ON 是否需要认证
// USER_AUTH_TYPE 认证类型
// USER_AUTH_KEY 认证识别号
// REQUIRE_AUTH_MODULE 需要认证模块
// NOT_AUTH_MODULE 无需认证模块
// USER_AUTH_GATEWAY 认证网关
// RBAC_DB_DSN 数据库连接DSN
// RBAC_ROLE_TABLE 角色表名称
// RBAC_USER_TABLE 用户表名称
// RBAC_ACCESS_TABLE 权限表名称
// RBAC_NODE_TABLE 节点表名称
/*
 * -- --------------------------------------------------------
 * 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;
 */
class Rbac
{
    // 认证方法
    static public function authenticate($map, $model = '')
    {
        if (empty($model)) {
            $model = C('USER_AUTH_MODEL');
        }
        
        // 使用给定的Map进行认证
        return M($model)->where($map)->find();
    }
    
    // 用于检测用户权限的方法,并保存到Session中
    static function saveAccessList($authId = null)
    {
        if (null === $authId) {
            $authId = $_SESSION[C('USER_AUTH_KEY')];
        }
        // 如果使用普通权限模式,保存当前用户的访问权限列表
        // 对管理员开发所有权限
        if (C('USER_AUTH_TYPE') != 2 && ! $_SESSION[C('ADMIN_AUTH_KEY')]) {
            $_SESSION['_ACCESS_LIST'] = self::getAccessList($authId);
        }
        
        return;
    }
    
    // 取得模块的所属记录访问权限列表 返回有权限的记录ID数组
    static function getRecordAccessList($authId = null, $module = '')
    {
        if (null === $authId) {
            $authId = $_SESSION[C('USER_AUTH_KEY')];
        }
        if (empty($module)) {
            $module = CONTROLLER_NAME;
        }
        // 获取权限访问列表
        $accessList = self::getModuleAccessList($authId, $module);
        
        return $accessList;
    }
    
    // 检查当前操作是否需要认证
    static function checkAccess()
    {
        // 如果项目要求认证,并且当前模块需要认证,则进行权限认证
        if (C('USER_AUTH_ON')) {
            $_module = array();
            $_action = array();
            if ("" != C('REQUIRE_AUTH_MODULE')) {
                // 需要认证的模块
                $_module['yes'] = explode(',', strtoupper(C('REQUIRE_AUTH_MODULE')));
            } else {
                // 无需认证的模块
                $_module['no'] = explode(',', strtoupper(C('NOT_AUTH_MODULE')));
            }
            // 检查当前模块是否需要认证
            if ((! empty($_module['no']) && ! in_array(strtoupper(CONTROLLER_NAME), $_module['no'])) || (! empty($_module['yes']) && in_array(strtoupper(CONTROLLER_NAME), $_module['yes']))) {
                if ("" != C('REQUIRE_AUTH_ACTION')) {
                    // 需要认证的操作
                    $_action['yes'] = explode(',', strtoupper(C('REQUIRE_AUTH_ACTION')));
                } else {
                    // 无需认证的操作
                    $_action['no'] = explode(',', strtoupper(C('NOT_AUTH_ACTION')));
                }
                // 检查当前操作是否需要认证
                if ((! empty($_action['no']) && ! in_array(strtoupper(ACTION_NAME), $_action['no'])) || (! empty($_action['yes']) && in_array(strtoupper(ACTION_NAME), $_action['yes']))) {
                    return true;
                } else {
                    return false;
                }
            } else {
                return false;
            }
        }
        
        return false;
    }
    
    // 登录检查
    static public function checkLogin()
    {
        // 检查当前操作是否需要认证
        if (self::checkAccess()) {
            // 检查认证识别号
            if (! $_SESSION[C('USER_AUTH_KEY')]) {
                if (C('GUEST_AUTH_ON')) {
                    // 开启游客授权访问
                    if (! isset($_SESSION['_ACCESS_LIST'])) // 保存游客权限
{
                        self::saveAccessList(C('GUEST_AUTH_ID'));
                    }
                } else {
                    // 禁止游客访问跳转到认证网关
                    redirect(PHP_FILE . C('USER_AUTH_GATEWAY'));
                }
            }
        }
        
        return true;
    }
    
    // 权限认证的过滤器方法
    static public function AccessDecision($appName = MODULE_NAME)
    {
        // 检查是否需要认证
        if (self::checkAccess()) {
            // 存在认证识别号,则进行进一步的访问决策
            $accessGuid = md5($appName . CONTROLLER_NAME . ACTION_NAME);
            if (empty($_SESSION[C('ADMIN_AUTH_KEY')])) {
                if (C('USER_AUTH_TYPE') == 2) {
                    // 加强验证和即时验证模式 更加安全 后台权限修改可以即时生效
                    // 通过数据库进行访问检查
                    $accessList = self::getAccessList($_SESSION[C('USER_AUTH_KEY')]);
                } else {
                    // 如果是管理员或者当前操作已经认证过,无需再次认证
                    if ($_SESSION[$accessGuid]) {
                        return true;
                    }
                    // 登录验证模式,比较登录后保存的权限访问列表
                    $accessList = $_SESSION['_ACCESS_LIST'];
                }
                // 判断是否为组件化模式,如果是,验证其全模块名
                if (! isset($accessList[strtoupper($appName)][strtoupper(CONTROLLER_NAME)][strtoupper(ACTION_NAME)])) {
                    $_SESSION[$accessGuid] = false;
                    
                    return false;
                } else {
                    $_SESSION[$accessGuid] = true;
                }
            } else {
                // 管理员无需认证
                return true;
            }
        }
        
        return true;
    }

    /**
     * +----------------------------------------------------------
     * 取得当前认证号的所有权限列表
     * +----------------------------------------------------------
     * 
     * @param integer $authId
     *            用户ID
     *            +----------------------------------------------------------
     * @access public
     *         +----------------------------------------------------------
     */
    static public function getAccessList($authId)
    {
        // Db方式权限数据
        $db = Db::getInstance(C('RBAC_DB_DSN'));
        $table = array(
            'role' => C('RBAC_ROLE_TABLE'),
            'user' => C('RBAC_USER_TABLE'),
            'access' => C('RBAC_ACCESS_TABLE'),
            'node' => C('RBAC_NODE_TABLE')
        );
        $sql = "select node.id,node.name from " . $table['role'] . " as role," . $table['user'] . " as user," . $table['access'] . " as access ," . $table['node'] . " as node " . "where user.user_id='{$authId}' and user.role_id=role.id and ( access.role_id=role.id  or (access.role_id=role.pid and role.pid!=0 ) ) and role.status=1 and access.node_id=node.id and node.level=1 and node.status=1";
        $apps = $db->query($sql);
        $access = array();
        foreach ($apps as $key => $app) {
            $appId = $app['id'];
            $appName = $app['name'];
            // 读取项目的模块权限
            $access[strtoupper($appName)] = array();
            $sql = "select node.id,node.name from " . $table['role'] . " as role," . $table['user'] . " as user," . $table['access'] . " as access ," . $table['node'] . " as node " . "where user.user_id='{$authId}' and user.role_id=role.id and ( access.role_id=role.id  or (access.role_id=role.pid and role.pid!=0 ) ) and role.status=1 and access.node_id=node.id and node.level=2 and node.pid={$appId} and node.status=1";
            $modules = $db->query($sql);
            // 判断是否存在公共模块的权限
            $publicAction = array();
            foreach ($modules as $key => $module) {
                $moduleId = $module['id'];
                $moduleName = $module['name'];
                if ('PUBLIC' == strtoupper($moduleName)) {
                    $sql = "select node.id,node.name from " . $table['role'] . " as role," . $table['user'] . " as user," . $table['access'] . " as access ," . $table['node'] . " as node " . "where user.user_id='{$authId}' and user.role_id=role.id and ( access.role_id=role.id  or (access.role_id=role.pid and role.pid!=0 ) ) and role.status=1 and access.node_id=node.id and node.level=3 and node.pid={$moduleId} and node.status=1";
                    $rs = $db->query($sql);
                    foreach ($rs as $a) {
                        $publicAction[$a['name']] = $a['id'];
                    }
                    unset($modules[$key]);
                    break;
                }
            }
            // 依次读取模块的操作权限
            foreach ($modules as $key => $module) {
                $moduleId = $module['id'];
                $moduleName = $module['name'];
                $sql = "select node.id,node.name from " . $table['role'] . " as role," . $table['user'] . " as user," . $table['access'] . " as access ," . $table['node'] . " as node " . "where user.user_id='{$authId}' and user.role_id=role.id and ( access.role_id=role.id  or (access.role_id=role.pid and role.pid!=0 ) ) and role.status=1 and access.node_id=node.id and node.level=3 and node.pid={$moduleId} and node.status=1";
                $rs = $db->query($sql);
                $action = array();
                foreach ($rs as $a) {
                    $action[$a['name']] = $a['id'];
                }
                // 和公共模块的操作权限合并
                $action += $publicAction;
                $access[strtoupper($appName)][strtoupper($moduleName)] = array_change_key_case($action, CASE_UPPER);
            }
        }
        
        return $access;
    }
    
    // 读取模块所属的记录访问权限
    static public function getModuleAccessList($authId, $module)
    {
        // Db方式
        $db = Db::getInstance(C('RBAC_DB_DSN'));
        $table = array(
            'role' => C('RBAC_ROLE_TABLE'),
            'user' => C('RBAC_USER_TABLE'),
            'access' => C('RBAC_ACCESS_TABLE')
        );
        $sql = "select access.node_id from " . $table['role'] . " as role," . $table['user'] . " as user," . $table['access'] . " as access " . "where user.user_id='{$authId}' and user.role_id=role.id and ( access.role_id=role.id  or (access.role_id=role.pid and role.pid!=0 ) ) and role.status=1 and  access.module='{$module}' and access.status=1";
        $rs = $db->query($sql);
        $access = array();
        foreach ($rs as $node) {
            $access[] = $node['node_id'];
        }
        
        return $access;
    }
}