7.7 与页面控制器共舞

到现在为止,我们已经知道如何验证用户身份、如何查找用户角色以及如何判断用户可以访问系统的哪些部分。现在我们要在页面控制器中使用它们。

[Note]Note

由于有很多方法用来处理访问控制,而且又因为大多数方法都止于特定应用。所以我们这里也只是列举两种应用。

7.7.1 天网恢恢

实现访问控制的一种方法是把它作为页面控制器调度周期的一部分,让它自动地检测用户的权限并且提前改变被请求动作的行为。这种方法在每个动作调用之前自动地检测用户的权限。

相应的代码如下:

<?php
abstract class Vendor_Controller_Page extends Solar_Controller_Page
{
    public $user;
    
    protected function _setup()
    {
        parent::_setup();
        $this->user = Solar_Registry::get('user');
    }
    
    protected function _preAction()
    {
        parent::_preAction();
        if (! $this->user->access->isAllowed($this, $this->_action)) {
            $this->_action = 'forbidden';
        }
    }
    
    public function actionForbidden()
    {
        $this->_view = null;
        $this->_response->setStatusCode(403);
        $this->_response->setContent('Access denied.');
    }
}

在上面的代码中,我们使用当前Vendor类继承Solar_Controller_Page类。并且实现以下逻辑:

  • 作为构造器的一部分,_setup()方法在对象注册表中获取Solar_User对象,因此我们就能得于当前用户的身份、角色和访问控制信息。(回忆一下,实例化Solar_User对象时,同时会为我们实例化验证对象。)

  • _preAction()方法在所有动作之前运行。实现了逻辑的一部分,它检测当前用户是否有权限访问当前控制器和动作。如果没有,它将会改变$this->_action的值,使得用户被重定向actionForbidden()方法,而不是当前动作。

  • actionForbidden()方法关闭视图和布局,设置HTTP状态码为"403 Forbidden"并且强制重置页面内容。(当然你可以创建forbidden.php视图来设置响应内容,而不是直接设置响应内容。)

现在,当用户试图访问无权限的控制器和动作时,系统将会响应一个"403 Forbidden"页面。

7.7.2 动作内部的访问控制

上面的方法非常简便,但是它没有体现更细微的差别。有时候你可能需要在当前运行的动作中检查访问权限。如果是这样,你可能希望创建一个特定方法在动作内部检测访问权限。例如:

<?php
abstract class Vendor_Controller_Page extends Solar_Controller_Page
{
    public $user;
    
    protected function _setup()
    {
        parent::_setup();
        $this->user = Solar_Registry::get('user');
    }
    
    protected function _isUserAllowed()
    {
        if ($this->user->access->isAllowed($this, $this->_action)) {
            return true;
        } else {
            $this->_error('Access denied.');
            $this->_response->setStatusCode(403);
            return false;
        }
    }
}

class Vendor_App_Foo extends Vendor_Controller_Page
{
    public function actionFoo()
    {
        // ... perform preliminary logic ...
        
        // check access
        if (! $this->_isUserAllowed()) {
            return;
        }
        
        // ... rest of the action here ...
    }
}

在这种方法中,我们做两件事。首先,使用当前Vendor类继承Solar_Controller_Page类。并且实现以下逻辑:

  • 作为构造器的一部分,_setup()方法在对象注册表中获取Solar_User对象,因此我们就能得于当前用户的身份、角色和访问控制信息。(回忆一下,实例化Solar_User对象时,同时会为我们实例化验证对象。)

  • _isUserAllowed()方法检测当前用户是否有权限访问当前控制器和动作。如果有,它返回真;但是如果没有,它调用Solar_Controller_Page::_error()方法且设置HTTP状态码为"403 Forbidden"。

基本的页面控制器弄好之后,我们修改actionBar()方法,在逻辑中间检验用户权限。如果用户有权限,那么程序继续执行。如果没有,则退出当前动作,且在_isUserAllowed()方法中调用_error()方法并且自动渲染错误页面视图。