在我的Symfony 2应用程序中,我有3个不同的用户角色,可以访问后端管理部分:
role_hierarchy:
ROLE_STAFF: ROLE_USER
ROLE_MODERATOR: ROLE_STAFF
ROLE_ADMIN: ROLE_MODERATOR
对于像http://example.org/admin/post/
这样的路线,我希望我的应用根据用户角色显示不同的信息,这意味着 3个控制器绑定到唯一的路径
处理此问题的最佳方法是什么?
我正在考虑一些解决方案,但似乎没有一个对我好:
一个控制器,在每个操作中我只测试用户角色:
<?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);
}
}
即使这样做,IMO显然也不是一个好的设计。
一个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;
}
}
与第一名相同。
我试图让控制器相互扩展:
<?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)。
在我的BlogBundle中为kernel.controller添加一个监听器,它将完成与2号相同的工作?
我正在寻找最好的设计和更灵活的解决方案,我们有可能在未来添加更多角色。
答案 0 :(得分:1)
恕我直言,你不会根据角色为相同的路线发射不同的控制器。这只是不同的责任。路由用于选择控制器,角色用于特权。一年后你将不记得这个伎俩,即。当你尝试添加新角色时。
当然,不同角色的不同内容问题经常出现,所以我最喜欢的解决方案是:
答案 1 :(得分: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 ...
由于我没有您的设置,我没有测试上面的代码。 顺便问一下:发布不同方法的区别是什么?
答案 2 :(得分:0)
请参阅http://symfony.com/doc/current/book/internals.html#kernel-controller-event
应该做的,并确保注入security.context服务
答案 3 :(得分:0)
vendor/symfony/symfony/src/Symfony/Component/Routing/Router.php
中的
可以选择替换matcher_class
中可能存在的config.yml
。
如果你继承UrlMatcher
和overRide matchRequest
,它将优先于Path匹配(仅限url)。
matchRequest
接受参数$ request(请求对象)
Request对象应包含安全提供程序侦听器在路由器侦听器之前运行的用户信息,并允许您通过组合URL和用户角色来选择路由。路由存储在按名称索引的数组中,因此名称必须不同。
您可以使用post_index[USER]
post_index[STAFF]
post_index[MODERATOR]
为了生成{{ path('post_index', {...}) }}
的网址,您还需要替换URLGenerator
的子类,并使用generator_class
选项将其注入路由器。