如何缓存 CakePHP Db ACL 检查

在将 SQL 查询日志记录到 Chrome 控制台中后,我开始注意到对 ARO 表的查询运行速度为 300 毫秒或更长。这些数据不会经常更改,并且每次请求都需要,因此它是缓存的理想选择。不幸的是,我找不到任何缓存 DbAcl 检查的机制。这是我的解决方案。

CacheDbAcl

这相当简单。首先在 app/Lib/CacheDbAcl.php 中创建一个新文件,并 复制并粘贴代码 (超链接) 或查看文章底部。

接下来将以下配置添加到 app/Config/bootstrap.php:Configure::write(‘CacheDbAclConfig’,’default’); > Configure::write(‘CacheDbAclAro’,’User.UserGroup’);<br > > <p>现在在 app Config/core.php 中添加或编辑以下内容:Configure::write(‘Acl.classname’, ‘CacheDbAcl’); ><br > 代码说明

CacheDbAcl 库与 CakePHP 2.x 附带的 DbAcl 库的功能完全相同。唯一更改的是 check() 方法。它的作用

  1. CacheDbAcl 验证是否启用了缓存。如果未启用,它将恢复正常操作。
  2. 读取要使用的缓存配置名称,请参见 CacheDbAclConfig。
  3. 检查要使用 ARO 的哪一部分作为缓存键(稍后详细介绍),请参见 CacheDbAclAro。
  4. 检查缓存中是否存在 Aro、Aco 和 Action 的条目。如果不存在,它将执行其正常操作,然后缓存结果。否则,它只从缓存中读取。

关于 CacheDbAclAro 设置的更多信息

由于传递到 DbAcl::check 的 ARO 数组可能因您的数据库结构和应用程序要求而异,因此我允许您设置 ARO 的哪一部分用作唯一的缓存键。如果您希望为每个用户创建缓存,只需完全不设置此设置。

如果您只希望为每个组创建缓存条目,请使用类似于我上面发布的内容。也许您有一个特殊用例,只希望使用 ARO 的非常特定部分作为键,这为您提供了这种灵活性。

我为这个应用程序选择了 User.UserGroup,因为我们的权限是基于组的,我希望减少创建的缓存条目的总数。当组的权限发生更改时,UserGroups 模型中的 modified 字段将自然地更新,因此新的缓存条目将自动创建,而旧的缓存条目将最终过期。

我的原始博客文章

/**
 *
 * Licensed under The MIT License
 * For full copyright and license information, please see the LICENSE.txt
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright     Copyright (c) Chris Nizzardini
 * @link          https://cakephp.com.cn CakePHP(tm) Project
 * @package       Cake.Controller.Component.Acl
 * @license       MIT License (https://www.opensource.org/licenses/mit-license.php)
 */
App::uses('AclInterface', 'Controller/Component/Acl');
App::uses('Hash', 'Utility');
App::uses('ClassRegistry', 'Utility');

/**
 * CacheDbAcl works the exact sabe as DbAcl expect that it will cache the results to using a cache config of your choosing:
 * - Configurations in bootstrap.php:
 * App::uses('CacheDbAcl', 'Lib');
 * Configure::write('CacheDbAclConfig','Name_of_Your_Cache_Config')
 * Configure::write('CacheDbAclAro','YourArrayKey.YourArray');
 *
 * - Configurations in core.php:
 * Configure::write('Acl.classname', 'CacheDbAcl');
 *
 * @package       Cake.Controller.Component.Acl
 */
class CacheDbAcl extends Object implements AclInterface {

/**
 * Constructor
 *
 */
    public function __construct() {
            parent::__construct();
            $this->Permission = ClassRegistry::init(array('class' => 'Permission', 'alias' => 'Permission'));
            $this->Aro = $this->Permission->Aro;
            $this->Aco = $this->Permission->Aco;
    }

/**
 * Initializes the containing component and sets the Aro/Aco objects to it.
 *
 * @param AclComponent $component
 * @return void
 */
    public function initialize(Component $component) {
            $component->Aro = $this->Aro;
            $component->Aco = $this->Aco;
    }

/**
 * Checks if the given $aro has access to action $action in $aco
 *
 * @param string $aro ARO The requesting object identifier.
 * @param string $aco ACO The controlled object identifier.
 * @param string $action Action (defaults to *)
 * @return boolean Success (true if ARO has access to action in ACO, false otherwise)
 * @link https://book.cakephp.com.cn/2.0/en/core-libraries/components/access-control-lists.html#checking-permissions-the-acl-component
 */
    public function check($aro, $aco, $action = "*") {

        // if cache is disabled then default to normal operation
        if(Configure::read('Cache.disable') == true){
            return $this->Permission->check($aro, $aco, $action);
        }

        // read name of cache config for AclCache
        $cacheConfig = Configure::read('CacheDbAclConfig');
        // if not found then use default
        if(!$cacheConfig){
            $cacheConfig = 'default';
        }

        // check which portion of $aro to use for key
        $cacheAro = Configure::read('CacheDbAclAro');
        // if not set just serialze $aro
        if(!$cacheAro){
            $cacheKey = 'CacheDbAcl_'.md5(serialize($aro).$aco.$action);
        }
        // use custom portion of $aro
        else{
            $tmp = explode('.', $cacheAro);
            $aroTmp = false;
            foreach($tmp as $i){
                if($aroTmp == false){
                    $aroTmp = $aro[$i];
                }
                else{
                    $aroTmp = $aroTmp[$i];
                }
            }

            if(!isset($aroTmp) || empty($aroTmp)){
                $cacheKey = 'CacheDbAcl_'.md5(serialize($aro).$aco.$action);
            }
            else{
                $cacheKey = 'CacheDbAcl_'.md5(serialize($aroTmp).$aco.$action);
            }
        }

        // check for cache key in cache
        $check = Cache::read($cacheKey);

        // if key exists then return value
        if( $check !== false ){
            return $check;
        }
        // check database and write to cache
        else{
            $check = $this->Permission->check($aro, $aco, $action);
            Cache::write($cacheKey,$check,$cacheConfig);
        }

            return $check;
    }

/**
 * Allow $aro to have access to action $actions in $aco
 *
 * @param string $aro ARO The requesting object identifier.
 * @param string $aco ACO The controlled object identifier.
 * @param string $actions Action (defaults to *)
 * @param integer $value Value to indicate access type (1 to give access, -1 to deny, 0 to inherit)
 * @return boolean Success
 * @link https://book.cakephp.com.cn/2.0/en/core-libraries/components/access-control-lists.html#assigning-permissions
 */
    public function allow($aro, $aco, $actions = "*", $value = 1) {
            return $this->Permission->allow($aro, $aco, $actions, $value);
    }

/**
 * Deny access for $aro to action $action in $aco
 *
 * @param string $aro ARO The requesting object identifier.
 * @param string $aco ACO The controlled object identifier.
 * @param string $action Action (defaults to *)
 * @return boolean Success
 * @link https://book.cakephp.com.cn/2.0/en/core-libraries/components/access-control-lists.html#assigning-permissions
 */
    public function deny($aro, $aco, $action = "*") {
            return $this->allow($aro, $aco, $action, -1);
    }

/**
 * Let access for $aro to action $action in $aco be inherited
 *
 * @param string $aro ARO The requesting object identifier.
 * @param string $aco ACO The controlled object identifier.
 * @param string $action Action (defaults to *)
 * @return boolean Success
 */
    public function inherit($aro, $aco, $action = "*") {
            return $this->allow($aro, $aco, $action, 0);
    }

/**
 * Allow $aro to have access to action $actions in $aco
 *
 * @param string $aro ARO The requesting object identifier.
 * @param string $aco ACO The controlled object identifier.
 * @param string $action Action (defaults to *)
 * @return boolean Success
 * @see allow()
 */
    public function grant($aro, $aco, $action = "*") {
            return $this->allow($aro, $aco, $action);
    }

/**
 * Deny access for $aro to action $action in $aco
 *
 * @param string $aro ARO The requesting object identifier.
 * @param string $aco ACO The controlled object identifier.
 * @param string $action Action (defaults to *)
 * @return boolean Success
 * @see deny()
 */
    public function revoke($aro, $aco, $action = "*") {
            return $this->deny($aro, $aco, $action);
    }

/**
 * Get an array of access-control links between the given Aro and Aco
 *
 * @param string $aro ARO The requesting object identifier.
 * @param string $aco ACO The controlled object identifier.
 * @return array Indexed array with: 'aro', 'aco' and 'link'
 */
    public function getAclLink($aro, $aco) {
            return $this->Permission->getAclLink($aro, $aco);
    }

/**
 * Get the keys used in an ACO
 *
 * @param array $keys Permission model info
 * @return array ACO keys
 */
    protected function _getAcoKeys($keys) {
            return $this->Permission->getAcoKeys($keys);
    }

}