检查Symfony

时间:2016-12-08 14:49:22

标签: php symfony

我有一个用户对象,其中包含已启用的属性'。我希望每个操作在继续之前首先检查用户是否已启用。

现在我已经使用每个其他控制器扩展的控制器解决了这个问题,但是使用setContainer函数来捕获每个控制器操作都感觉非常糟糕。

class BaseController extends Controller{

    public function setContainer(ContainerInterface $container = null)
    {
         $this->container = $container;

         $user = $this->getUser();

         // Redirect disabled users to a info page
         if (!$user->isEnabled() && !$this instanceof InfoController) {
             return $this->redirectToRoute('path_to_info');
         }
}

我尝试使用之前的过滤器(http://symfony.com/doc/current/event_dispatcher/before_after_filters.html)来构建它,但无法获取User对象..有哪些提示?

编辑:

这是我的解决方案:

namespace AppBundle\Security;

use AppBundle\Controller\AccessDeniedController;
use AppBundle\Controller\ConfirmController;

use Symfony\Bundle\FrameworkBundle\Routing\Router;
use Symfony\Bundle\TwigBundle\Controller\ExceptionController;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;

class UserEnabledListener
{
    private $tokenStorage;
    private $router;

    public function __construct(TokenStorage $tokenStorage, Router $router)
    {
        $this->tokenStorage = $tokenStorage;
        $this->router = $router;
    }

    public function onKernelController(FilterControllerEvent $event)
    {
        $controller = $event->getController();

        /*
         * $controller passed can be either a class or a Closure.
         * This is not usual in Symfony but it may happen.
         * If it is a class, it comes in array format
         */
        if (!is_array($controller)) {
            return;
        }

        $controller = $controller[0];

        // Skip enabled check when:
        // - we are already are the AccessDenied controller, or
        // - user confirms e-mail and becomes enabled again, or
        // - Twig throws error in template
        if ($controller instanceof AccessDeniedController ||
            $controller instanceof ConfirmController ||
            $controller instanceof ExceptionController) {
            return;
        }

        $user = $this->tokenStorage->getToken()->getUser();

        // Show info page when user is disabled
        if (!$user->isEnabled()) {

            $redirectUrl = $this->router->generate('warning');

            $event->setController(function() use ($redirectUrl) {
                return new RedirectResponse($redirectUrl);
            });
        }
    }
}

编辑2: 好的,所以事实证明手动检查每个控制器真的很糟糕,因为你会错过来自第三方依赖的控制器。我将使用安全注释,并在自定义的异常控制器或模板等中执行进一步的自定义逻辑。

3 个答案:

答案 0 :(得分:4)

您可以使用event listener收听任何新请求。

您需要注入用户然后进行验证:

    <service id="my_request_listener" class="Namespace\MyListener">
        <tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" />
        <argument type="service" id="security.token_storage" />
    </service>

编辑:这是一个提供示例的代码段

class MyRequestListener {
private $tokenStorage;

public function __construct(TokenStorage $tokenStorage)
{
    $this->tokenStorage = $tokenStorage;
}

public function onKernelRequest(GetResponseEvent $event)
{
    if (!$event->getRequest()->isMasterRequest()) {
        // don't do anything if it's not the master request
        return;
    }

    if ($this->tokenStorage->getToken()) {
        $user = $this->tokenStorage->getToken()->getUser();
        //do your verification here
    }

}

答案 1 :(得分:3)

在你的情况下,我会使用@Security注释,如果你使用表达式语言,它可以非常灵活。

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;

/**
 * @Security("user.isEnabled()")
 */
class EventController extends Controller 
{
    // ...
}

最后,每个控制器文件中只有1行,并且它具有非常易读的优点(项目新开发人员可以立即知道发生了什么,而无需去检查BaseController的内容或过滤器之前的任何潜力...)

有关此here的更多文档。

答案 2 :(得分:-2)

您也可以在getuser()中覆盖BaseController功能。

/**
 * Get a user from the Security Token Storage.
 *
 * @return mixed
 *
 * @throws \LogicException If SecurityBundle is not available
 *
 * @see TokenInterface::getUser()
 */
protected function getUser()
{
    if (!$this->container->has('security.token_storage')) {
        throw new \LogicException('The SecurityBundle is not registered in your application.');
    }
    if (null === $token = $this->container->get('security.token_storage')->getToken()) {
        return;
    }
    if (!is_object($user = $token->getUser())) {
        // e.g. anonymous authentication
        return;
    }

    // Redirect disabled users to a info page
     if (!$user->isEnabled() && !$this instanceof InfoController) {
         return $this->redirectToRoute('path_to_info');
     }

     return $user;
}