2

我正在尝试使用 Yii 构建一个站点,发现自己处于需要对 Yii 中的访问控制隐喻进行一些澄清的情况。具体来说,可以覆盖的后代中的accessRules方法CController

先决条件:

这是 Gii 默认生成的代码,与文档非常相似:

public function accessRules()
{
    return array(
        array('allow',  // allow all users to perform 'index' and 'view' actions
            'actions'=>array('index','view'),
            'users'=>array('*'),
        ),
        array('allow', // allow authenticated user to perform 'create' and 'update' actions
            'actions'=>array('create','update'),
            'users'=>array('@'),
        ),
        array('allow', // allow admin user to perform 'admin' and 'delete' actions
            'actions'=>array('admin','delete'),
            'roles'=> array('admin'),
        ),
        array('deny',  // deny all users
            'users'=>array('*'),
        ),
    );
}

问题:

我不清楚的是——为什么我们需要为许多不同的用户和组以及不同的操作定义访问规则,而不是只检查一个当前用户和操作的权限?- 这里用斜体突出显示我的主要问题,以防读者(答案作者)可能不太清楚。

此代码在特定请求的上下文中执行,为此,特定controller、特定action和特定user是已知的。例如,如果用户是来宾,我看不出有任何理由为“管理员”角色或经过身份验证的用户定义规则。

推理:

经过一番阐述,我想出了以下实现:

public function accessRules()
{
    return array(
      array(Yii::app()->user->hasRights()?'allow':'deny'),
      );
}

其中hasRights是添加到CWebUser后代中的简单自定义方法:

class WebUser extends CWebUser
{
  private $ACL = // ACL example
    array('user' => // controller id
      array('index' => User::AC_MODERATOR, // action ids
            'view' => User::AC_MODERATOR,
            'create' => User::AC_MODERATOR,
            'update' => User::AC_ADMIN,
            'delete' => User::AC_ADMIN,
            'admin' => User::AC_ADMIN),
           // ...
      );

  public function hasRights()
  {
    return (Yii::app()->user->getState('accessRights') >=
      $this->ACL[Yii::app()->controller->id][Yii::app()->controller->action->id]);
  }
}

如您所见,hasRights使用当前用户“权限”(照常从数据库读取)、当前控制器和操作来计算单个布尔值truefalse值作为访问决策。

这种方法有什么问题?为什么 Yii 默认不使用这样简单的东西?

The Gii-generated accessRules above do not only look excessive, but also imply that access rules are scattered among many controllers. In my approach a single and compact ACL is used.

4

2 回答 2

3

Here's where I think you're confused.

First of all, Yii is in fact only looking at the current user and the current action. Just like how you got the current permissions for the controller by grabbing a nested array, indexed by the controller ID, Yii is creating an instance of your controller and only looking at those access rules. Also, just like you're only looking at the permissions of a particular action, Yii is only looking at the rules pertaining to the current action. It accesses those permissions just as easily as you return the value of the nested array.

As far as the user goes, it's also only looking at the current user; the current user name, the current user role, etc. The difference is, there are multiple attributes Yii will allow within a rule, in contrast to the single permission value associated with the user.

It seems what you don't like about this approach is you think permissions should be handled all in one place. This could be simpler in some cases, but more difficult in others. What happens when you have multiple controllers and multiple actions in each controller? You now have a very large array to manage, that references data from multiple different contexts. In the way Yii does it, the current controller has control over how it's data structure can be accessed. This lines up with Yii's MVC structure, and the concept of encapsulation.

Your solution is more elegant in some respects, but it hinges on the idea that permissions will only ever need to be structured in a cascading one directional structure. What I mean is your permissions are like one long hallway, with doors separating one area from another. If the user doesn't have the key to one door, they shouldn't be able to access the next, etc. But what happens if, in your example, you need a user to both view content, and update it without being able to create new content? This is a much more complicated scenario which will need to be handled using roles. So then you'd have to process rules within your array, much like Yii does. Except instead of Yii's object oriented approach, you've got everything embedded in one long array.

So perhaps your solution will work in some cases, but I'm sure you can see why Yii chose its approach since it will work, by default, for far more situations.

于 2013-08-02T22:32:25.513 回答
0

Yii implements a hierarchical RBAC scheme via its CWebApplication ->authManager application component, which actually similar to what you did,

Take a look at this

EDIT

"let us suppose that we use RBAC and add some rules into the accessRules method, such as with admin role in the example above." 

you don't add rules into accessRules method when you use RBAC

what you do is , you define authorization hierarchy which involves three steps 1)define authorization itmes which includes roles and operations, this would be stored in DB,in authitem table

Eg. Operations

siteAddProduct->in this case 'site' is the controller name and addProduct is the action in 'site' controller
sitedeleteProduct ...etc

Roles

admin,editor...etc

2)Estabish relationship between authorization items(between role and operations) this would be stored in authitemchild table

eg.

admin -> siteAddProduct
editor -> sitedeleteProduct
admin->editor

3)assign roles to application users this would be stored in authassignment table

for example userid 1 ->admin

now in your base controller( assuming that you have a controller class from which all other controllers in your app extneds from ) you override beforeAction() method and check the logged in user(current user) permission using

Yii::app()->user->checkAccess($operation);

$operation is the requested controller + action

you can get the controller name using $this->id and action name using $this->getAction()->id

于 2013-01-09T21:18:32.327 回答