公司的客服系统是用 Yii 开发的,权限系统是基于 Yii CDbAuthManager 开发的,随着权限项目的增加和权限组以及人员的增多,导致加入页面的时候打开很慢,因为没进入一个页面会去检查一下该用户对于该操作的权限。下面是用XHProf查看的结果部分截图,会发现CDbAuthManager::checkAccessCDbAuthManager::checkAccessRecursive 等CDbAuthManager 方法会调用很多次,而 CDbAuthManage 是基于数据库,所以每次调用还要去进行数据库的查询,这样难免会给性能带来瓶颈。(该图只要关注第一和第二列就可以了,其他列是优化过的结果且本机的测试数据库)。

利用Redis 加速 Yii CDbAuthManage利用 Redis 加速 Yii CDbAuthManager

再来看下CDbAuthManager::checkAccessRecursive这个方法的实现,这个方法会去递归的查询数据库来验证用户的授权项目。

protected function checkAccessRecursive($itemName, $userId, $params, $assignments) {
                if (($item = $this->getAuthItem($itemName)) === null)
                        return false;
                Yii::trace('Checking permission "' . $item->getName() . '"', 'system.web.auth.CDbAuthManager');
                if ($this->executeBizRule($item->getBizRule(), $params, $item->getData())) {
                        if (in_array($itemName, $this->defaultRoles))
                                return true;
                        if (isset($assignments[$itemName])) {
                                $assignment = $assignments[$itemName];
                                if ($this->executeBizRule($assignment->getBizRule(), $params, $assignment->getData()))
                                        return true;
                        }
                        $parents = $this->db->createCommand()
                                ->select('parent')
                                ->from($this->itemChildTable)
                                ->where('child=:name', array(':name' => $itemName))
                                ->queryColumn();
                        foreach ($parents as $parent) {
                                if ($this->checkAccessRecursive($parent, $userId, $params, $assignments))
                                        return true;
                        }
                }
                return false;
        }

CDbAuthManager的其他对授权项目的操作都是基于数据库的读写,具体源码实现:http://code.google.com/p/yii/source/browse/tags/1.1.10/framework/web/auth/CDbAuthManager.php

好了,现在要做的就是把CDbAuthManager中的对数据库的操作进行改装成缓存中的操作,这样就可以大大的提高验证时的执行熟读了,同时加一个生成缓存的脚本:/protected/commands/AuthCacheCommand.php 来跑一下这个脚本生成缓存数据。 表对于的 Redis 的数据结构主要是:

  • 表:auth_item_child =>;Redis Set

  • 表:auth_item => Redis String

  • 表:auth_assignment => Redis Hash

AuthCacheCommand.php 源码:

<?php

class AuthCacheCommand extends CConsoleCommand {

        public function run($args) {
                $items = Yii::app()->db->createCommand()
                        ->select('*')
                        ->from('kf_auth_item_child')
                        ->queryAll();

                foreach ($items as $item) {
                        $item['parent'] = strtolower($item['parent']);
                        $item['child'] = strtolower($item['child']);
                        Yii::app()->redis->sAdd("authItemChild:" . $item['parent'], $item['child']);
                        Yii::app()->redis->sAdd("authItemChildParent:" . $item['child'], $item['parent']);
                }


                $rows = Yii::app()->db->createCommand()
                        ->select()
                        ->from('kf_auth_item')
                        ->queryAll();

                foreach ($rows as $row) {
                        if (($data = @unserialize($row['data'])) === false)
                                $data = null;
                        $cache = array(
                            'type' => $row['type'],
                            'description' => $row['description'],
                            'bizrule' => $row['bizrule'],
                            'data' => serialize($data)
                        );
                        Yii::app()->redis->set('authItem:' . strtolower($row['name']), serialize($cache));
                }
        }
}

改装后的CDbAuthManager源码:

<?php

class UCDbAuthManager extends CAuthManager {

        /**
         * @var string the ID of the {@link CDbConnection} application component. Defaults to 'db'.
         * The database must have the tables as declared in "framework/web/auth/*.sql".
         */
        public $connectionID = 'db';

        /**
         * @var string the name of the table storing authorization items. Defaults to 'AuthItem'.
         */
        public $itemTable = 'AuthItem';

        /**
         * @var string the name of the table storing authorization item hierarchy. Defaults to 'AuthItemChild'.
         */
        public $itemChildTable = 'AuthItemChild';

        /**
         * @var string the name of the table storing authorization item assignments. Defaults to 'AuthAssignment'.
         */
        public $assignmentTable = 'AuthAssignment';

        /**
         * @var CDbConnection the database connection. By default, this is initialized
         * automatically as the application component whose ID is indicated as {@link connectionID}.
         */
        public $connectionRedis = 'redis';
        public $db;
        private $_usingSqlite;
        public $redis;
        private static $_data;

        /**
         * Initializes the application component.
         * This method overrides the parent implementation by establishing the database connection.
         */
        public function init() {
                parent::init();
                $this->getRedisConnection();
                $this->_usingSqlite = !strncmp($this->getDbConnection()->getDriverName(), 'sqlite', 6);
        }

        /**
         * Performs access check for the specified user.
         * @param string $itemName the name of the operation that need access check
         * @param mixed $userId the user ID. This should can be either an integer and a string representing
         * the unique identifier of a user. See {@link IWebUser::getId}.
         * @param array $params name-value pairs that would be passed to biz rules associated
         * with the tasks and roles assigned to the user.
         * @return boolean whether the operations can be performed by the user.
         */
        public function checkAccess($itemName, $userId, $params = array()) {
                $assignments = $this->getAuthAssignments($userId);
                return $this->checkAccessRecursive($itemName, $userId, $params, $assignments);
        }

        /**
         * Performs access check for the specified user.
         * This method is internally called by {@link checkAccess}.
         * @param string $itemName the name of the operation that need access check
         * @param mixed $userId the user ID. This should can be either an integer and a string representing
         * the unique identifier of a user. See {@link IWebUser::getId}.
         * @param array $params name-value pairs that would be passed to biz rules associated
         * with the tasks and roles assigned to the user.
         * @param array $assignments the assignments to the specified user
         * @return boolean whether the operations can be performed by the user.
         * @since 1.1.3
         */
        protected function checkAccessRecursive($itemName, $userId, $params, $assignments) {
                $itemKey = md5($itemName . "|" . $userId . "|" . http_build_query($params) . "|" . serialize($assignments));

                if (isset(self::$_data[$itemKey]))
                        return self::$_data[$itemKey];
                if (($item = $this->getAuthItem($itemName)) === null) {
                        self::$_data[$itemKey] = false;
                        return false;
                }

                if ($this->executeBizRule($item->getBizRule(), $params, $item->getData())) {
                        if (in_array($itemName, $this->defaultRoles)) {
                                self::$_data[$itemKey] = true;
                                return true;
                        }
                        if (isset($assignments[$itemName])) {
                                $assignment = $assignments[$itemName];
                                if ($this->executeBizRule($assignment->getBizRule(), $params, $assignment->getData())) {
                                        self::$_data[$itemKey] = true;
                                        return true;
                                }
                        }
                        // 读取 set authItemChild 缓存
                        $parents = $this->redis->sMembers('authItemChildParent:' . strtolower($itemName));


                        foreach ($parents as $parent) {
                                //$this->redis->sAdd("authItemChild:" . $parent, $itemName);
                                if ($this->checkAccessRecursive($parent, $userId, $params, $assignments)) {
                                        self::$_data[$itemKey] = true;
                                        return true;
                                }
                        }
                }
                self::$_data[$itemKey] = false;
                return false;
        }

        /**
         * Adds an item as a child of another item.
         * @param string $itemName the parent item name
         * @param string $childName the child item name
         * @throws CException if either parent or child doesn't exist or if a loop has been detected.
         */
        public function addItemChild($itemName, $childName) {
                if ($itemName === $childName)
                        throw new CException(Yii::t('yii', 'Cannot add "{name}" as a child of itself.', array('{name}' => $itemName)));

                // 先从string authItem 缓存中读取
                if ($this->redis->get('authItem:' . $itemName)) {
                        $rows[0] = unserialize($this->redis->get('authItem:' . $itemName));
                        $rows[0]['name'] = $itemName;
                }
                if ($this->redis->get('authItem:' . $childName)) {
                        $rows[1] = unserialize($this->redis->get('authItem:' . $childName));
                        $rows[1]['name'] = $childName;
                }


                if (count($rows) == 2) {
                        if ($rows[0]['name'] === $itemName) {
                                $parentType = $rows[0]['type'];
                                $childType = $rows[1]['type'];
                        } else {
                                $childType = $rows[0]['type'];
                                $parentType = $rows[1]['type'];
                        }

                        $this->checkItemChildType($parentType, $childType);

                        if ($this->detectLoop($itemName, $childName))
                                throw new CException(Yii::t('yii', 'Cannot add "{child}" as a child of "{name}". A loop has been detected.', array('{child}' => $childName, '{name}' => $itemName)));
                        $this->db->createCommand()
                                ->insert($this->itemChildTable, array(
                                    'parent' => $itemName,
                                    'child' => $childName,
                        ));

                        // 写入 set authItemChild 缓存
                        $this->redis->sAdd('authItemChild:' . strtolower($itemName), strtolower($childName));
                        $this->redis->sAdd('authItemChildParent:' . strtolower($childName), strtolower($itemName));
                }
                else {
                        exit();
                        throw new CException(Yii::t('yii', 'Either "{parent}" or "{child}" does not exist.', array('{child}' => $childName, '{parent}' => $itemName)));
                }
        }

        /**
         * Removes a child from its parent.
         * Note, the child item is not deleted. Only the parent-child relationship is removed.
         * @param string $itemName the parent item name
         * @param string $childName the child item name
         * @return boolean whether the removal is successful
         */
        public function removeItemChild($itemName, $childName) {
                // 删除对应的 set authItemChild 缓存
                $this->redis->sRem('authItemChild:' . strtolower($itemName), strtolower($childName));
                $this->redis->sRem('authItemChildParent:' . strtolower($childName), strtolower($itemName));
                return $this->db->createCommand()
                                ->delete($this->itemChildTable, 'parent=:parent AND child=:child', array(
                                    ':parent' => $itemName,
                                    ':child' => $childName
                                )) > 0;
        }

        /**
         * Returns a value indicating whether a child exists within a parent.
         * @param string $itemName the parent item name
         * @param string $childName the child item name
         * @return boolean whether the child exists
         */
        public function hasItemChild($itemName, $childName) {
                return $this->redis->sIsMember('authItemChild:' . strtolower($itemName), strtolower($childName));
        }

        /**
         * Returns the children of the specified item.
         * @param mixed $names the parent item name. This can be either a string or an array.
         * The latter represents a list of item names (available since version 1.0.5).
         * @return array all child items of the parent
         */
        public function getItemChildren($names) {
                $cacheChildren = array();
                if (is_string($names)) {
                        $condition = 'parent=' . $this->db->quoteValue($names);
                        //取缓存
                        $cacheChildren[] = $this->redis->sMembers('authItemChild:' . strtolower($names));
                } else if (is_array($names) && $names !== array()) {
                        foreach ($names as &$name) {
                                $name = $this->db->quoteValue($name);
                                //取缓存
                                $cacheChildren[] = $this->redis->sMembers('authItemChild:' . strtolower($name));
                        }

                        $condition = 'parent IN (' . implode(', ', $names) . ')';
                }
                $rows = array();

                if (count($cacheChildren) >= 2) {
                        foreach ($cacheChildren as $cacheChild) {
                                foreach ($cacheChild as $child) {
                                        $tmp = unserialize($this->redis->get('authItem:' . $child));
                                        $tmp['name'] = $child;
                                        $rows[] = $tmp;
                                }
                        }
                } else {
                        $rows = $this->db->createCommand()
                                ->select('name, type, description, bizrule, data')
                                ->from(array(
                                    $this->itemTable,
                                    $this->itemChildTable
                                ))
                                ->where($condition . ' AND name=child')
                                ->queryAll();
                }



                $children = array();
                foreach ($rows as $row) {
                        if (($data = @unserialize($row['data'])) === false)
                                $data = null;
                        $children[$row['name']] = new CAuthItem($this, $row['name'], $row['type'], $row['description'], $row['bizrule'], $data);
                }
                return $children;
        }

        /**
         * Assigns an authorization item to a user.
         * @param string $itemName the item name
         * @param mixed $userId the user ID (see {@link IWebUser::getId})
         * @param string $bizRule the business rule to be executed when {@link checkAccess} is called
         * for this particular authorization item.
         * @param mixed $data additional data associated with this assignment
         * @return CAuthAssignment the authorization assignment information.
         * @throws CException if the item does not exist or if the item has already been assigned to the user
         */
        public function assign($itemName, $userId, $bizRule = null, $data = null) {
                if ($this->usingSqlite() && $this->getAuthItem($itemName) === null)
                        throw new CException(Yii::t('yii', 'The item "{name}" does not exist.', array('{name}' => $itemName)));

                $this->db->createCommand()
                        ->insert($this->assignmentTable, array(
                            'itemname' => $itemName,
                            'userid' => $userId,
                            'bizrule' => $bizRule,
                            'data' => serialize($data)
                ));
                // 同时写入hash authAssignment缓存
                $assignment = new CAuthAssignment($this, $itemName, $userId, $bizRule, $data);
                $this->redis->hSet('authAssignment:' . $userId, $itemName, serialize($assignment));
                return $assignment;
        }

        /**
         * Revokes an authorization assignment from a user.
         * @param string $itemName the item name
         * @param mixed $userId the user ID (see {@link IWebUser::getId})
         * @return boolean whether removal is successful
         */
        public function revoke($itemName, $userId) {
                // 同时删除 hash authAssignment缓存
                $this->redis->hDel('authAssignment:' . $userId, $itemName);
                $result = $this->db->createCommand()
                        ->delete($this->assignmentTable, 'itemname=:itemname AND userid=:userid', array(
                    ':itemname' => $itemName,
                    ':userid' => $userId
                ));
                return $result;
        }

        /**
         * Returns a value indicating whether the item has been assigned to the user.
         * @param string $itemName the item name
         * @param mixed $userId the user ID (see {@link IWebUser::getId})
         * @return boolean whether the item has been assigned to the user.
         */
        public function isAssigned($itemName, $userId) {
                return $this->redis->hGet('authAssignment:' . $userId, $itemName);
        }

        /**
         * Returns the item assignment information.
         * @param string $itemName the item name
         * @param mixed $userId the user ID (see {@link IWebUser::getId})
         * @return CAuthAssignment the item assignment information. Null is returned if
         * the item is not assigned to the user.
         */
        public function getAuthAssignment($itemName, $userId) {
                // 首先从hash authAssignment缓存中读取
                if ($this->redis->hGet('authAssignment:' . $userId, $itemName)) {
                        return unserialize($this->redis->hGet('authAssignment:' . $userId, $itemName));
                } else {
                        // 读库
                        $row = $this->db->createCommand()
                                ->select()
                                ->from($this->assignmentTable)
                                ->where('itemname=:itemname AND userid=:userid', array(
                                    ':itemname' => $itemName,
                                    ':userid' => $userId))
                                ->queryRow();
                        if ($row !== false) {
                                if (($data = @unserialize($row['data'])) === false)
                                        $data = null;
                                // 同时写到hash authAssignment缓存中
                                $assignment = new CAuthAssignment($this, $row['itemname'], $row['userid'], $row['bizrule'], $data);
                                $this->redis->hSet('authAssignment:' . $userId, $row['itemname'], serialize($assignment));
                                return $assignment;
                        }
                        else
                                return null;
                }
        }

        /**
         * Returns the item assignments for the specified user.
         * @param mixed $userId the user ID (see {@link IWebUser::getId})
         * @return array the item assignment information for the user. An empty array will be
         * returned if there is no item assigned to the user.
         */
        public function getAuthAssignments($userId) {
                if (true || !$this->redis->hLen('authAssignment:' . $userId)) {
                        // 没有 hash authAssignment 缓存 则读库
                        $rows = $this->db->createCommand()
                                ->select()
                                ->from($this->assignmentTable)
                                ->where('userid=:userid', array(':userid' => $userId))
                                ->queryAll();
                        $assignments = array();

                        foreach ($rows as $row) {
                                if (($data = @unserialize($row['data'])) === false)
                                        $data = null;
                                $assignments[$row['itemname']] = new CAuthAssignment($this, $row['itemname'], $row['userid'], $row['bizrule'], $data);
                                // 写入 hash authAssignment 缓存
                                $this->redis->hSet('authAssignment:' . $userId, $row['itemname'], serialize($assignments[$row['itemname']]));
                        }
                }else {
                        // 有 hash authAssignment 缓存 则读缓存
                        $assignments = array();
                        $rows = $this->redis->hGetAll('authAssignment:' . $userId);
                        foreach ($rows as $key => $value) {
                                $assignments[$key] = unserialize($value);
                        }
                }

                return $assignments;
        }

        /**
         * Saves the changes to an authorization assignment.
         * @param CAuthAssignment $assignment the assignment that has been changed.
         */
        public function saveAuthAssignment($assignment) {
                $this->db->createCommand()
                        ->update($this->assignmentTable, array(
                            'bizrule' => $assignment->getBizRule(),
                            'data' => serialize($assignment->getData()),
                                ), 'itemname=:itemname AND userid=:userid', array(
                            'itemname' => $assignment->getItemName(),
                            'userid' => $assignment->getUserId()
                ));
                // 更新AuthAssignment 的同时更新 用户的hash authAssignment缓存
                if (($data = $assignment->getData()) === false)
                        $data = null;
                $this->redis->hDel('authAssignment:' . $assignment->getUserId(), $assignment->getItemName());
                $assignments = new CAuthAssignment($this, $assignment->getItemName(), $assignment->getUserId(), $assignment->getBizRule(), $data);
                $this->redis->hSet('authAssignment:' . $assignment->getUserId(), $assignment->getItemName(), serialize($assignments));
        }

        /**
         * Returns the authorization items of the specific type and user.
         * @param integer $type the item type (0: operation, 1: task, 2: role). Defaults to null,
         * meaning returning all items regardless of their type.
         * @param mixed $userId the user ID. Defaults to null, meaning returning all items even if
         * they are not assigned to a user.
         * @return array the authorization items of the specific type.
         */
        public function getAuthItems($type = null, $userId = null) {
                if ($type === null && $userId === null) {
                        $command = $this->db->createCommand()
                                ->select()
                                ->from($this->itemTable);
                } else if ($userId === null) {
                        $command = $this->db->createCommand()
                                ->select()
                                ->from($this->itemTable)
                                ->where('type=:type', array(':type' => $type));
                } else if ($type === null) {
                        $command = $this->db->createCommand()
                                ->select('name,type,description,t1.bizrule,t1.data')
                                ->from(array(
                                    $this->itemTable . ' t1',
                                    $this->assignmentTable . ' t2'
                                ))
                                ->where('name=itemname AND userid=:userid', array(':userid' => $userId));
                } else {
                        $command = $this->db->createCommand()
                                ->select('name,type,description,t1.bizrule,t1.data')
                                ->from(array(
                                    $this->itemTable . ' t1',
                                    $this->assignmentTable . ' t2'
                                ))
                                ->where('name=itemname AND type=:type AND userid=:userid', array(
                            ':type' => $type,
                            ':userid' => $userId
                        ));
                }
                $items = array();
                foreach ($command->queryAll() as $row) {
                        if (($data = @unserialize($row['data'])) === false)
                                $data = null;
                        $items[$row['name']] = new CAuthItem($this, $row['name'], $row['type'], $row['description'], $row['bizrule'], $data);
                }
                return $items;
        }

        /**
         * Creates an authorization item.
         * An authorization item represents an action permission (e.g. creating a post).
         * It has three types: operation, task and role.
         * Authorization items form a hierarchy. Higher level items inheirt permissions representing
         * by lower level items.
         * @param string $name the item name. This must be a unique identifier.
         * @param integer $type the item type (0: operation, 1: task, 2: role).
         * @param string $description description of the item
         * @param string $bizRule business rule associated with the item. This is a piece of
         * PHP code that will be executed when {@link checkAccess} is called for the item.
         * @param mixed $data additional data associated with the item.
         * @return CAuthItem the authorization item
         * @throws CException if an item with the same name already exists
         */
        public function createAuthItem($name, $type, $description = '', $bizRule = null, $data = null) {
                $this->db->createCommand()
                        ->insert($this->itemTable, array(
                            'name' => $name,
                            'type' => $type,
                            'description' => $description,
                            'bizrule' => $bizRule,
                            'data' => serialize($data)
                ));

                // 写入 string authItem缓存
                $cache = array(
                    'type' => $type,
                    'description' => $description,
                    'bizrule' => $bizRule,
                    'data' => serialize($data)
                );
                $this->redis->set('authItem:' . $name, serialize($cache));
                return new CAuthItem($this, $name, $type, $description, $bizRule, $data);
        }

        /**
         * Removes the specified authorization item.
         * @param string $name the name of the item to be removed
         * @return boolean whether the item exists in the storage and has been removed
         */
        public function removeAuthItem($name) {

                if ($this->usingSqlite()) {
                        $this->db->createCommand()
                                ->delete($this->itemChildTable, 'parent=:name1 OR child=:name2', array(
                                    ':name1' => $name,
                                    ':name2' => $name
                        ));

                        // 删除 set 为 $name 的 authItemChild 缓存
                        $this->redis->delete('authItemChild:' . strtolower($name));
                        $this->redis->delete('authItemChildParent:' .strtolower($name));

                        $this->db->createCommand()
                                ->delete($this->assignmentTable, 'itemname=:name', array(
                                    ':name' => $name,
                        ));
                        // 删除 hkey 为 $name 的 uathAssignment 缓存
                        $keys = $this->redis->keys("authAssignment:*");
                        if ($keys) {
                                foreach ($keys as $key) {
                                        $hKeys = $this->redis->hKeys($key);
                                        if ($hKeys) {
                                                foreach ($hKeys as $hkey) {
                                                        if ($hkey == $name) {
                                                                $this->redis->hDel($key, $hkey);
                                                        }
                                                }
                                        }
                                }
                        }
                }

                // 删除 set authItem 缓存
                $this->redis->delete('authItem:' . $name);
                return $this->db->createCommand()
                                ->delete($this->itemTable, 'name=:name', array(
                                    ':name' => $name
                                )) > 0;
        }

        /**
         * Returns the authorization item with the specified name.
         * @param string $name the name of the item
         * @return CAuthItem the authorization item. Null if the item cannot be found.
         */
        public function getAuthItem($name) {
                if ($this->redis->get('authItem:' . $name)) {
                        // 先读取 string authItem缓存
                        $row = unserialize($this->redis->get('authItem:' . $name));
                        $row['name'] = $name;
                } else {
                        $row = $this->db->createCommand()
                                ->select()
                                ->from($this->itemTable)
                                ->where('name=:name', array(':name' => $name))
                                ->queryRow();
                }




                if ($row !== false) {
                        if (($data = @unserialize($row['data'])) === false)
                                $data = null;
                        //回写缓存
                        $cache = array(
                            'type' => $row['type'],
                            'description' => $row['description'],
                            'bizrule' => $row['bizrule'],
                            'data' => serialize($data),
                        );
                        $this->redis->set('authItem:' . $name, serialize($cache));
                        return new CAuthItem($this, $row['name'], $row['type'], $row['description'], $row['bizrule'], $data);
                }
                else
                        return null;
        }

        /**
         * Saves an authorization item to persistent storage.
         * @param CAuthItem $item the item to be saved.
         * @param string $oldName the old item name. If null, it means the item name is not changed.
         */
        public function saveAuthItem($item, $oldName = null) {
                if ($this->usingSqlite() && $oldName !== null && $item->getName() !== $oldName) {
                        $this->db->createCommand()
                                ->update($this->itemChildTable, array(
                                    'parent' => $item->getName(),
                                        ), 'parent=:whereName', array(
                                    ':whereName' => $oldName,
                        ));

                        $this->db->createCommand()
                                ->update($this->itemChildTable, array(
                                    'child' => $item->getName(),
                                        ), 'child=:whereName', array(
                                    ':whereName' => $oldName,
                        ));
                        // 更新 set  authItemChild 缓存
                        $children = $this->redis->sMembers('authItemChild:' . strtolower($oldName));
                        $this->redis->delete('authItemChild:' . strtolower($oldName));
                        $this->redis->sAdd('authItemChild:' . strtolower($item->getName()), strtolower($children));

                        $parents = $this->redis->sMembers('authItemChildParent:' . strtolower($oldName));
                        $this->redis->delete('authItemChildParent:' . strtolower($oldName));
                        $this->redis->sAdd('authItemChildParent:' . strtolower($item->getName()), strtolower($parents));


                        $this->db->createCommand()
                                ->update($this->assignmentTable, array(
                                    'itemname' => $item->getName(),
                                        ), 'itemname=:whereName', array(
                                    ':whereName' => $oldName,
                        ));
                        // 同时更新 hash uathAssignment 缓存 中hkey为$oldName的缓存
                        $keys = $this->redis->keys("authAssignment:*");
                        if ($keys) {
                                foreach ($keys as $key) {
                                        $hKeys = $this->redis->hKeys($key);
                                        if ($hKeys) {
                                                foreach ($hKeys as $hkey) {
                                                        if ($hkey == $oldName) {
                                                                $value = $this->redis->hGet($key, $hkey);
                                                                $this->redis->hDel($key, $hkey);
                                                                $this->redis->hSet($key, $item->getName(), $value);
                                                        }
                                                }
                                        }
                                }
                        }
                }

                $this->db->createCommand()
                        ->update($this->itemTable, array(
                            'name' => $item->getName(),
                            'type' => $item->getType(),
                            'description' => $item->getDescription(),
                            'bizrule' => $item->getBizRule(),
                            'data' => serialize($item->getData()),
                                ), 'name=:whereName', array(
                            ':whereName' => $oldName === null ? $item->getName() : $oldName,
                ));
                //  更新string authItem 缓存
                $name = $oldName === null ? $item->getName() : $oldName;
                $value = $this->redis->get('authItem:' . $name);
                if ($value) {
                        $this->redis->delete('authItem:' . $name);
                        $cache = array(
                            'type' => $item->getType(),
                            'description' => $item->getDescription(),
                            'bizrule' => $item->getBizRule(),
                            'data' => serialize($item->getData()),
                        );
                        $this->redis->set('authItem:' . $item->getName(), serialize($cache));
                }
        }

        /**
         * Saves the authorization data to persistent storage.
         */
        public function save() {

        }

        /**
         * Removes all authorization data.
         */
        public function clearAll() {
                $this->clearAuthAssignments();
                $this->db->createCommand()->delete($this->itemChildTable);
                // 删除所有set  缓存
                $this->deleteAllRedisKeys("authItemChild:*");
                $this->deleteAllRedisKeys("authItemChildParent:*");
                $this->db->createCommand()->delete($this->itemTable);
                // 删除所有string authItem 缓存
                $this->deleteAllRedisKeys("authItem:*");
        }

        /**
         * Removes all authorization assignments.
         */
        public function clearAuthAssignments() {
                $this->db->createCommand()->delete($this->assignmentTable);
                // 删除所有hash authAssignment 缓存
                $this->deleteAllRedisKeys("authAssignment:*");
        }

        /**
         * Checks whether there is a loop in the authorization item hierarchy.
         * @param string $itemName parent item name
         * @param string $childName the name of the child item that is to be added to the hierarchy
         * @return boolean whether a loop exists
         */
        protected function detectLoop($itemName, $childName) {
                if ($childName === $itemName)
                        return true;
                foreach ($this->getItemChildren($childName) as $child) {
                        if ($this->detectLoop($itemName, $child->getName()))
                                return true;
                }
                return false;
        }

        /**
         * @return CDbConnection the DB connection instance
         * @throws CException if {@link connectionID} does not point to a valid application component.
         */
        protected function getDbConnection() {
                if ($this->db !== null)
                        return $this->db;
                else if (($this->db = Yii::app()->getComponent($this->connectionID)) instanceof CDbConnection)
                        return $this->db;
                else
                        throw new CException(Yii::t('yii', 'CDbAuthManager.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.', array('{id}' => $this->connectionID)));
        }

        protected function getRedisConnection() {
                if ($this->redis !== null)
                        return $this->redis;
                else if (($this->redis = Yii::app()->getComponent($this->connectionRedis)) instanceof URedis)
                        return $this->redis;
                else
                        throw new CException(Yii::t('yii', 'UCDbAuthManager.connectionRedis "{id}" is invalid.', array('{id}' => $this->connectionRedis)));
        }

        /**
         * @return boolean whether the database is a SQLite database
         */
        protected function usingSqlite() {
                return $this->_usingSqlite;
        }

        /**
         * 删除所有的模糊匹配的redis key
         * @param string $keyString
         */
        protected function deleteAllRedisKeys($keyString) {
                $keys = $this->redis->keys($keyString);
                if ($keys) {
                        foreach ($keys as $key) {
                                $this->redis->delete($key);
                        }
                }
        }

}

加入配置文件

        'authManager' => array(
            'class' => '你存放得位置/UCDbAuthManager',
            'connectionID' => 'db',
            'connectionRedis' => 'redis',
            'itemTable' => 'kf_auth_item',
            'assignmentTable' => 'kf_auth_assignment',
            'itemChildTable' => 'kf_auth_item_child'
        ),

OK,结束!

转载请注明: 转载自Yuansir-web 菜鸟 | LAMP 学习笔记

本文链接地址: 利用 Redis 加速 Yii CDbAuthManager

知识共享许可协议 本作品采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可