2

我有两个主要的表:

  1. 书籍(id,作者,isbn,...)
  2. 用户(ID,用户名,密码,...)

我正在考虑构建一个应用程序,其中 User1 登录并可以:

  1. 查看所有书籍的列表(例如,只有标题)
  2. 仅查看他应该有权访问的某些书籍的详细信息(作者、isbn、...)

每个用户都可以访问特定的书籍集。我不需要各种角色。

我已经为上述设置了 MVC 和关系(habtm)。我现在正在考虑开发权限。您认为 CakePHP ACL 解决了这个问题还是有点矫枉过正?

如果它是矫枉过正,是否有另一种组件或更简单的方法来构建所需的功能?

4

2 回答 2

1

是的,ACL 是多余的

ACL 是一个非常强大和灵活的系统——但它不是免费的,它带来了复杂性。除非您有一个绝对需要细粒度权限的用例(您描述的两个规则不适合此) - 不要使用 ACL。

限制预订他添加的用户

这条规则很容易实现——例如添加到相关的查找调用中:

$results = $BookModelInstance->find('all', array(
    'conditions' => array(
        'created_by' => AuthComponent::user('id')
    )
));

限制他购买的用户的书籍

这条规则也很容易实现,稍微复杂一点:

$BookModelInstance->bindModel(array(
    'hasOne' => array( // Yes, hasOne not hasMany
        'MyPurchase' => array(
            'className' => 'Purchase',
            'foriegnKey' => 'user_id'
        ) 
    )
));
$results = $BookModelInstance->find('all', array(
    'recursive' => 0, // to join hasOne+belongsTo associations into the query
    'conditions' => array(
        'MyPurchase.user_id' = AuthComponent::user('id'),
    )
));

bindModel 调用等效于SELECT .. FROM books LEFT JOIN book_users.. find 调用中的条件将因此将结果限制为有用户购买图书记录的图书。

把它们放在一起

自动应用这两个规则的简单实现是:

model Book extends AppModel {

    public $actsAs = array('Containable');

    public $restrictToUser = true;


    public function beforeSave($options = array()) {
        if (!$this->id) {
           // Store who created this book
           $this->data[$this->alias]['created_by'] = AuthComponent::user('id');
        }
        return true;
    }

    public function beforeFind($queryData) {
        if (!$this->restrictToUser) {
            // we don't want to apply user-level restrictions
            return true;
        }

        $userId = AuthComponent::user('id');
        if (!$userId) {
            // we want to restrict to the current user - there isn't one.
            return false;
        }

        // define the association to the purchase table
        $this->bindModel(array(
            'hasOne' => array(
                'MyPurchase' => array(
                    'className' => 'Purchase',
                    'foriegnKey' => 'user_id'
                ) 
            )
        ));

        //ensure the purchase table is included in the current query
        $queryData['contain']['MyPurchase'] = array();

        // restrict to rows created by the current user, OR purchased by the current user
        $queryData['conditions']['OR'] = array(
            $this->alias '.created_by' => $userId,
            'MyPurchase.user_id' => $userId
        );
        return $queryData;
    }
}

这需要在 books 表中存在一个字段created_by(或等效字段),并使用可包含来确保该purchases表(或等效字段)包含在所有相关查询中。

于 2013-01-13T13:39:31.147 回答
0

最简单的解决方案:在控制器中添加一个条件,因此:

$this->set('books', $this->Book->find(
    'all',
    array('conditions' => array('Book.user_id' => $user['User']['id']))
);

缺点:您可能会在这里创建重复的代码,因为此检查也必须在其他地方进行。此外,当您开始测试模型时,您只能测试它是否返回书籍,而不能测试模型方法,例如:getMyBooks($userId)。所以不,不是首选解决方案。

下一个解决方案:签入模型

它可以通过签入来完成,例如您的书籍模型。您可以只检查 afterfind() 方法是否允许返回的记录。在您的之前查找中,您还可以为所有查询添加附加条件。

一般来说,模型应该是胖的,所以我建议在那里实现明确的方法,例如:getAllBooks、getBooksOfUser($User)、getLatestBooksOfUser($User) 等。

为什么这是一个很好的实现?因为您现在在一个中心位置管理访问级别。您可以测试该模型,并确定它只返回该用户的书籍。

使用 beforeSave 等,您可以干预每次保存尝试并首先检查:嘿,您想保存它,但这真的是您的书吗?

ACL 解决方案

但总的来说,实施一些 ACL 解决方案(最好是内置的)可能是明智的,因为这可以让您的应用程序更加面向未来。它允许灵活性,例如:

每个用户都可以访问特定的书籍集。我不需要各种角色。

现在是这样,但未来可以改变它。因此,如果您需要快速解决方案,只需自定义过滤记录。但是想想未来。

于 2013-01-13T12:12:26.230 回答