2012-04-17 51 views
8

在我Symfony的2个应用程序我有3个不同的用户角色,可以访问后台管理部分:绑定一个路线根据用户角色不同的控制器

role_hierarchy: 
    ROLE_STAFF:  ROLE_USER 
    ROLE_MODERATOR: ROLE_STAFF 
    ROLE_ADMIN:  ROLE_MODERATOR 

对于路线像http://example.org/admin/post/,我会就像我的应用根据用户角色显示不同的信息,这意味着需要绑定到路由

处理这个问题的最佳方法是什么?

我曾想过一些解决方案,但没有一个似乎对我来说是很好的:

  1. 一个控制器,并在每个动作我只是测试用户角色:

    <?php 
    
    /** 
    * @Route("/admin/post") 
    */ 
    class PostController extends Controller 
    { 
        /** 
        * Lists all post entities. 
        * 
        * @Route("/", name="post_index") 
        * @Template() 
        * @Secure(roles="ROLE_STAFF") 
        */ 
        public function indexAction() 
        { 
         $user = $this->get('security.context')->getToken()->getUser(); 
    
         if ($this->get('security.context')->isGranted('ROLE_STAFF')) { 
          // Do ROLE_STAFF related stuff 
         } else if ($this->get('security.context')->isGranted('ROLE_MODERATOR')) { 
          // Do ROLE_MODERATOR related stuff 
         } else if ($this->get('security.context')->isGranted('ROLE_ADMIN')) { 
          // Do ROLE_ADMIN related stuff 
         } 
    
         return array('posts' => $posts); 
        } 
    } 
    

    即使是不这项工作,国际海事组织显然不是一个好设计。

  2. 一个BackendController其分派到3个不同的控制器:

    <?php 
    
    /** 
    * @Route("/admin/post") 
    */ 
    class PostBackendController extends Controller 
    { 
        /** 
        * Lists all post entities. 
        * 
        * @Route("", name="admin_post_index") 
        * @Template("AcmeBlogBundle:PostAdmin:index.html.twig") 
        * @Secure(roles="ROLE_STAFF") 
        */ 
        public function indexAction() 
        { 
         if ($this->get('security.context')->isGranted('ROLE_STAFF')) { 
          $response = $this->forward('AcmeBlogBundle:PostStaff:index'); 
         } else if ($this->get('security.context')->isGranted('ROLE_MODERATOR')) { 
          $response = $this->forward('AcmeBlogBundle:PostModerator:index'); 
         } else if ($this->get('security.context')->isGranted('ROLE_ADMIN')) { 
          $response = $this->forward('AcmeBlogBundle:PostAdmin:index'); 
         } 
    
         return $response; 
        } 
    } 
    

    同一个号码。

  3. 我试图让控制器扩展对方:

    <?php 
    
    /** 
    * @Route("/admin/post") 
    */ 
    class PostStaffController extends Controller 
    { 
        /** 
        * Lists all post entities. 
        * 
        * @Route("/", name="post_index") 
        * @Template() 
        * @Secure(roles="ROLE_STAFF") 
        */ 
        public function indexAction() 
        { 
         $user = $this->get('security.context')->getToken()->getUser(); 
    
         // Do ROLE_STAFF related stuff 
    
         return array('posts' => $posts); 
        } 
    } 
    
    <?php 
    
    /** 
    * @Route("/admin/post") 
    */ 
    class PostModeratorController extends PostStaffController 
    { 
        /** 
        * Lists all post entities. 
        * 
        * @Route("/", name="post_index") 
        * @Template() 
        * @Secure(roles="ROLE_MODERATOR") 
        */ 
        public function indexAction() 
        { 
         $user = $this->get('security.context')->getToken()->getUser(); 
    
         // As PostModeratorController extends PostStaffController, 
         // I can either use parent action or redefine it here 
    
         return array('posts' => $posts); 
        } 
    } 
    
    <?php 
    
    /** 
    * @Route("/admin/post") 
    */ 
    class PostAdminController extends PostModeratorController 
    { 
        /** 
        * Lists all post entities. 
        * 
        * @Route("/", name="post_index") 
        * @Template() 
        * @Secure(roles="ROLE_ADMIN") 
        */ 
        public function indexAction() 
        { 
         $user = $this->get('security.context')->getToken()->getUser(); 
    
         // Same applies here 
    
         return array('posts' => $posts); 
        } 
    } 
    

    IMO这是一个更好的设计,但我不能管理,使其工作。路由系统在匹配的第一个控制器上停止。我想让它自动扮演级联风格的王者(即,如果用户是员工,则转到PostStaffController,否则如果用户是主持人转到PostModeratorController,否则转到PostAdminController)。

  4. 在我的BlogBu​​ndle中添加一个监听器到kernel.controller,它将完成与编号2相同的工作?

我正在寻找最好的设计和更灵活的解决方案有有,我们在未来增加更多的角色的机会。

+0

我面对完全相同的情况,你有没有找到一个好的解决方案? – 2012-08-07 22:50:00

+0

所有解决方案都很好,但设计不好。 如果您遇到同样的问题,请确保首先对您的应用程序不构成错误概念。在我的情况下,而不是这样做,我为我的实体做了2种不同的表单类型:即“配置”和“定制”。所以管理员可以访问“配置”和“定制”控制器,而员工和主持人只能访问“定制”。 不确定是否清楚。也许我应该把它作为一个完整的答案? – iamdto 2012-08-24 11:08:51

+0

你是对的,这是一个糟糕的设计。我的解决方案是将应用程序的不同区域分开放在不同的包中,我可以相应地管理这些角色。谢谢。 – 2012-09-03 14:36:34

回答

0

第二种解决方案的自动化版本如何?像:

// Roles ordered from most to least significant (ROLE_ADMIN -> ROLE_MODERATOR -> etc) 
    $roles = $myUserProvider->getRoles(); 
    foreach ($roles as $role) { 
     // add a check to test, if the function you're calling really exists 
     $roleName = ucfirst(strtolower(mb_substr($role, 0, 5))); 
     $response = $this->forward(sprintf('AcmeBlogBundle:Post%s:index', $roleName)) 

     break; 
    } 

    // Check that $response is not null and do something with it ... 

由于我没有安装,我没有测试过上面的代码。 Btw:发布内容的不同方法有什么区别?

+0

假设您想在管理员区域查看帖子:您可以访问http://example.org/admin/post/ {id}'。版主将拥有较轻的视图版本,而管理员将看到有关该实体的全部**信息(版主是客户,管理员是我们公司的开发人员和人员)。我不想用很多'if'' else'来重载我的视图,表单类型,控制器等,这就是为什么我需要这种调度器。不知道这是否是一个好的设计决策,但这是我目前找到的唯一解决方案。 – iamdto 2012-04-20 11:43:26

0

vendor/symfony/symfony/src/Symfony/Component/Routing/Router.php

有更换matcher_class应在config.yml是可能的选项。

如果您将子类UrlMatcher和overRide matchRequest优先于Path匹配(仅限url)。

matchRequest需要一个参数$请求(Request对象)

Request对象应包含用户信息提供安全提供监听器运行路由器监听器之前,让你选择的路线相结合的网址和用户角色。路由存储在按名称索引的数组中,因此名称将需要不同。

你可能使用的名称,如post_index[USER]post_index[STAFF]post_index[MODERATOR]

为了生成{{ path('post_index', {...}) }}的URL,你还需要更换子类URLGenerator并注入是与generator_class选项路由器。

1

恕我直言,你不应该为基于角色的同一路线激发不同的控制器。这只是不同的责任。路由用于选择控制器,角色用于特权。一年后,你将不会记得这个技巧,即。当你尝试添加新的角色。

当然,不同内容的不同角色的问题是经常,所以在这种情况下,我最喜欢的解决方案是:

  1. 当不同角色的控制器有很大不同,我用不同的路由与重定向时需要。
  2. 当控制器类似但内容不同时,即。不同的数据库查询条件,我使用与您的解决方案相似的解决方案2.但是替代方法是,使用来自同一控制器的私有/受保护的方法来完成作业。有一个黑客 - 你必须从顶部到底部检查角色,即。首先检查ROLE_ADMIN,下一个ROLE_OPERATOR和最后一个ROLE_STAFF,因为当你的ROLE_ADMIN从ROLE_STAFF继承,然后阻止用户捕获它。
  3. 当差异只是在一些应该显示/隐藏不同角色的信息块中时,我会留在一个控制器中并检查模板中的角色以确定是否显示哪个块。
相关问题