控制器类上的@Security注释被操作方法覆盖

时间:2017-01-04 16:26:24

标签: php symfony security annotations

我有一个Symfony2控制器如下:

/**
 * @Security("is_granted('my_permission')")
 */
class MyController extends Controller 
{
    /**
     * @Security("is_granted('another_permission')")
     */
    public function myAction() 
    {
        // ...
    }
}

@Security方法上的myAction()注释会覆盖/忽略@Security类上的父MyController注释。有没有办法制作这些堆栈,以避免不得不这样做:

/**
 * @Security("is_granted('my_permission') and is_granted('another_permission')")
 */
public function myAction() 
{
    // ...
}

控制器中的每个操作方法?

2 个答案:

答案 0 :(得分:2)

  

myAction方法上的@Security注释似乎覆盖/忽略MyController类上的父@Security注释。

实际上,Sensio\Bundle\FrameworkExtraBundle\Configuration\Security注释不允许嵌套配置(请参阅allowArray()方法)。因此,方法配置会覆盖@Security注释的类配置。

  

有没有办法让这些堆叠......

不是一个简单的方法,你需要创建三个类和一个技巧来不重新实现整个父代码:

Security.php

namespace AppBundle\Configuration;

/**
 * @Annotation
 */
class Security extends \Sensio\Bundle\FrameworkExtraBundle\Configuration\Security
{
    public function getAliasName()
    {
        return 'app_security';
    }

    public function allowArray()
    {
        // allow nested configuration (class/method).
        return true;
    }
}

SecurityConfiguration.php

此类允许您通过所有安全配置(类/方法)组合最终安全表达式。

namespace AppBundle\Configuration;

class SecurityConfiguration
{
    /**
     * @var Security[]
     */
    private $configurations;

    public function __construct(array $configurations)
    {
        $this->configurations = $configurations;
    }

    public function getExpression()
    {
        $expressions = [];
        foreach ($this->configurations as $configuration) {
            $expressions[] = $configuration->getExpression();
        }

        return implode(' and ', $expressions);
    }
}

SecurityListener.php

namespace AppBundle\EventListener;

use AppBundle\Configuration\SecurityConfiguration;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\HttpKernel\KernelEvents;

class SecurityListener extends \Sensio\Bundle\FrameworkExtraBundle\EventListener\SecurityListener
{
    public function onKernelController(FilterControllerEvent $event)
    {
        $request = $event->getRequest();
        if (!$configuration = $request->attributes->get('_app_security')) {
            return;
        }

        // trick to simulate one security configuration (all in one class/method).
        $request->attributes->set('_security', new SecurityConfiguration($configuration));

        parent::onKernelController($event);
    }

    public static function getSubscribedEvents()
    {
        // this listener must be called after Sensio\Bundle\FrameworkExtraBundle\EventListener\ControllerListener.
        return array(KernelEvents::CONTROLLER => array('onKernelController', -1));
    }
}

services.yml

services:
    app.security.listener:
        class: AppBundle\EventListener\SecurityListener
        parent: sensio_framework_extra.security.listener
        tags:
            - { name: kernel.event_subscriber }

最后,只需使用您的@AppBundle\Configuration\Security注释而不是标准注释。

答案 1 :(得分:0)

这是我的尝试:

sp中使用此角色层次结构:

app/config/security.yml

如果我有两个用户:role_hierarchy: ROLE_CLASS: ROLE_CLASS ROLE_METHOD: [ROLE_CLASS, ROLE_METHOD] user1ROLE_CLASS user2(这意味着此用户同时拥有两个角色),那么第一个用户就可以看到控制器内创建的所有页面,除了那些有其他限制的页面。

控制器示例:

ROLE_METHOD

因为/** * @Security("is_granted('ROLE_CLASS')") */ class SomeController extends Controller { /** * @Route("/page1", name="page1") * @Security("is_granted('ROLE_METHOD')") */ public function page1() { return $this->render('default/page1.html.twig'); } /** * @Route("/page2", name="page2") */ public function page2() { return $this->render('default/page2.html.twig'); } } user1,他只能看到ROLE_CLASS,而不是/page2,因为他会收到403 /page1错误(显然是为了开发)。 另一方面,Expression "is_granted('ROLE_METHOD')" denied access.user2(和ROLE_METHOD),他能够看到两个页面。