【问题标题】:Redirect in parent controller在父控制器中重定向
【发布时间】:2017-06-22 15:12:20
【问题描述】:

在我的 ZF2 (2.4.5) 项目中,我的主(父)控制器具有验证用户权限的功能,因此每个继承的控制器都可以轻松访问它。但是重定向存在问题。我知道继承控制器的动作必须返回响应,但是可以在父控制器中强制重定向吗?

父控制器

<?php

namespace Application;

use Zend\Mvc\Controller\AbstractActionController;

class CoreController extends AbstractActionController{

    public function checkAccess($moduleName, $accessName){
        if($this->getAclService()->isAllowed($moduleName, $accessName)){
            self::redirect()->toRoute('access-denied');
        }
    }
}

继承的控制器

namespace Application\Controller;

use Application\CoreController;
use Zend\View\Model\ViewModel;

class InterfaceController extends CoreController{

    public function indexAction(){

        $this->checkAccess('Foo', 'Bar');

        return new ViewModel([

        ]);
    }
}

TL;DR 如果我在InterfaceController 中调用$this-&gt;checkAccess('Foo', 'Bar'); 和在CoreController 中调用$this-&gt;getAclService()-&gt;isAllowed($moduleName, $accessName) 返回false 我想立即将用户重定向到路由'access-denied' 而无需完成InterfaceController::indexAction 的其余部分

重要提示:我想避免检查 checkAccess 返回的内容,我只是强制重定向。

提前感谢您的回复。

【问题讨论】:

    标签: php zend-framework2 acl


    【解决方案1】:

    好的,我使用全局异常处理程序完成了这项工作

    儿童控制器

    <?php
    
    namespace Warehouse\Controller;
    
    use Application\CoreController;
    use Zend\View\Model\ViewModel;
    
    class IndexController extends CoreController {
    
        public function getWarehouseDocumentAction() {
            parent::checkAccess('Warehouse', 'incoming-goods');
    
            return new ViewModel([
                'foo' => 'bar',
            ]);
        }
    
    }
    

    父控制器

    namespace Application;
    
    use Application\Exception\InsufficientPermissionException;
    use Zend\Mvc\Controller\AbstractActionController;
    
    class CoreController extends AbstractActionController {
    
        public function checkAccess($moduleName, $accessName){
            if(!$this->getServiceLocator()->get(MyAcl::class)->isAllowed($moduleName, $accessName, $this->identity())){
                throw new InsufficientPermissionException('Access denied. Insufficient permission.');
            }
        }
    }
    

    模块.php

    <?php
    
    namespace Application;
    
    use Application\Exception\InsufficientPermissionException;
    use Application\Monolog\Handler\DoctrineLogMessageHandler;
    use Zend\Mvc\MvcEvent;
    
    class Module {
    
        public function onBootstrap(MvcEvent $e) {
    
            $sharedEvents = $e->getApplication()->getEventManager()->getSharedManager();
    
            $sharedEvents->attach('Zend\Mvc\Application', 'dispatch.error', function (MvcEvent $event) {
                if (php_sapi_name() !== 'cli') {
                    $exception = $event->getParam('exception');
                    if ($exception instanceof InsufficientPermissionException) {
                        $target = $event->getTarget();
                        return $target->redirect()->toRoute('access-denied');
                    }
                }
            });
    
        }
    }
    

    权限保存在数据库中。

    【讨论】:

    • 不错。我忘记的另一个选项是在父控制器中使用 init() 方法。这在任何子操作之前调用,并且可以启动重定向。只需确保在将控制权传递回子类之前进行重定向。
    • 用 EDIT #2 更新了我的答案
    • 嗯,据我了解,您应该在开始操作之前检查访问权限,但这样的事情不适合我。我的控制器有点大,我认为这是我的代码设计问题。例如,我有一个带有表单的视图,我想授予用户查看表单的访问权限,但在表单提交后阻止访问。我知道这不应该是这样,但代码 id 已经超过 1 岁,团队从 1 人成长为 6 人。此外,我们主要是创建新功能而不是重构代码。
    • 我明白了。是的,这与我们的代码库不同,因为我们使用 ACL 在 URL 级别进行限制。我们会进行详细检查(工作流程类型检查),但这对我们来说是在操作/视图级别。
    【解决方案2】:

    您正在执行一个简单的 302 http 重定向到“拒绝访问”,因此您可以只渲染响应对象并停止 php 执行:

    public function checkAccess($moduleName, $accessName){
        if (!$this->getAclService()->isAllowed($moduleName, $accessName))) {
            self::redirect()->toRoute('access-denied');
            $this->getResponse()->send();
            exit;
        } else {
            return true;
        }
    }
    

    你可以简单地抛出一个异常:

    public function checkAccess($moduleName, $accessName){
        if (!$this->getAclService()->isAllowed($moduleName, $accessName))) {
            self::redirect()->toRoute('access-denied');
            throw new \Exception('access denied');
        } else {
            return true;
        }
    }
    

    异常将阻止进一步的代码执行,重定向将阻止呈现异常错误页面。

    【讨论】:

      【解决方案3】:

      我说这是不可能的。调用代码 InterfaceController::indexAction 至少需要 return 来启动重定向过程。

      您可以通过让基本控制器设置重定向来稍微清理一下,但继承的控制器需要通过调用 return 来停止脚本执行。

      基本控制器

      use Zend\Mvc\Controller\AbstractActionController;
      
      class CoreController extends AbstractActionController{
      
          public function checkAccess($moduleName, $accessName){
              if (!$this->getAclService()->isAllowed($moduleName, $accessName))) {
                  self::redirect()->toRoute('access-denied');
                  return false;
              } else {
                  return true;
              }
          }
      }
      

      继承的控制器

      use Application\CoreController;
      use Zend\View\Model\ViewModel;
      
      class InterfaceController extends CoreController{
      
          public function indexAction(){
      
              if (!$this->checkAccess('Foo', 'Bar')) {
                  return;
              }
      
              return new ViewModel([
      
              ]);
          }
      }
      

      编辑

      附带说明一下,在我们公司中,我们在基本控制器的 init() 方法中进行 ACL 检查,并且没有被覆盖,因此可以在任何 action 代码运行之前立即进行重定向。

      编辑#2

      我完全忘记了我们使用的 init 方法。如果您正在寻找另一种解决方案,请尝试一下。

      基本控制器

      use Zend\Mvc\Controller\AbstractActionController;
      
      class CoreController extends AbstractActionController{
      
          public function init() {
              // bootstrap code...
      
              // This should redirect
              if (!$this->_isUserAuthorized()) {
                  return;
              }
      
              // If the ACL check was OK then pass control to controllers
              return parent::init();
          }
      
          private function _isUserAuthorized() {
              // checks done here
              // return true if OK
              // else
      
             $this->_response->setRedirect($this->view->defaultUrl($redirect))->sendResponse();
             return false;
          }
      }
      

      继承的控制器

      use Application\CoreController;
      use Zend\View\Model\ViewModel;
      
      class InterfaceController extends CoreController{
      
          public function indexAction(){
      
              // nothing to do here
      
              return new ViewModel([]);
          }
      }
      

      【讨论】:

      • 这正是我想要避免的。也许 EventManager 会有所帮助? @edit 我需要在单个操作中进行多次检查。
      猜你喜欢
      • 1970-01-01
      • 2012-10-27
      • 2015-02-20
      • 1970-01-01
      • 1970-01-01
      • 2011-11-23
      • 1970-01-01
      • 2013-11-30
      • 2012-07-16
      相关资源
      最近更新 更多