Symfony:如何为动态路由触发自定义安全投票器


Symfony: How to trigger custom security voter for dynamic routes

我有以下设置:

  • 动态路由加载器,它根据数据库中有关应用程序视图的信息生成路由并调用适当的控制器

  • 自定义安全投票器,用于验证用户是否应有权访问当前视图

我当然可以从适当的控制器中调用is_granted方法并且它可以工作。我想做的不是从控制器内部触发投票者(这需要在每个控制器中重复我自己只是使用不同的视图 ID),而是为路由加载器中生成的每个路由自动触发它。这样,我就不必执行每个路由基本相同的标准验证 - 检查用户是否有权访问此视图,但专注于业务逻辑,这可能需要满足额外的要求才能让用户访问此路由(这就是控制器中的内容)。

我的方向是路由生成方法中有一些参数:

    $route = new Route($path, $defaults, $requirements);
    $routes->add($routeName, $route);

这将允许我为创建的路由触发is_granted,但有关参数的文档很少。

有谁知道如何将这种要求传递给用户,例如,对于创建的路由要求用户is_granted('VIEW_1')

您可以将任何您想要的内容添加到默认数组中,它们将被传递。 例如:

cerad_game__project__game__update__by_scorer:
  path:  /project/{_project}/game/{_game}/update-by-scorer
  defaults:
    _role:       ROLE_SCORER_ADMIN
    _model:      cerad_game__project__game__update__by_scorer__model_factory
    _form:       cerad_game__project__game__update__by_scorer__form_factory
    _controller: cerad_game__project__game__update__by_scorer__controller:action
    _template: '@CeradGame/Project/Game/Update/ByScorer/GameUpdateByScorerTwigPage.html.twig'

然后,您将创建一个内核控制器侦听器,该侦听器将在解析控制器之后但在调用控制器操作方法之前调用。

class ModelEventListener extends ContainerAware implements EventSubscriberInterface {
public static function getSubscribedEvents()
{
    return array(
        KernelEvents::CONTROLLER => array(  
            array('onControllerRole',  self::ControllerRoleEventListenerPriority),
            array('onControllerModel', self::ControllerModelEventListenerPriority),
            array('onControllerForm',  self::ControllerFormEventListenerPriority),
        ),
        KernelEvents::VIEW => array(
            array('onView', self::ViewEventListenerPriority),
        ),
    );
}
public function onControllerRole(FilterControllerEvent $event)
{
    if (!$event->getRequest()->attributes->has('_role')) return;
    $role = $event->getRequest()->attributes->get('_role');
    $securityContext = $this->container->get('security.context');
    if (!$securityContext->isGranted($role))
    {
        // For public it redirects
        // For users we see the exception
        // die('access denied ' . $event->getRequest()->attributes->get('_route'));
        throw new AccessDeniedException(); 
    }
}

然后连接起来:

cerad_core__model_event_listener:
    class:  '%cerad_core__model_event_listener__class%'
    calls:
        - [setContainer, ['@service_container']]
    tags:
        - { name: kernel.event_subscriber }

记录在这里:http://symfony.com/doc/current/cookbook/event_dispatcher/event_listener.html

请注意,我在此处注入了容器,而不仅仅是安全上下文。 我需要其他方法的容器。 它也是 2.7 代码。 还没有开始将其更新到 3.0。