如何在 Symfony2 中动态处理角色|权限:使用动态角色限制函数
How to dynamic handle roles|permissions in Symfony2: restrict functions with dynamic roles
这个 post 旨在成为 post so you may have to read it to understand this question. Having that info and the answer from previous post and also having found this 有用的 UserBundle 的第二部分,它帮助我举例说明可能的关系 Many To Many
在 roles
和 users
之间 我可能会问:
- 我有动态角色,现在如何使用这些新角色
ROLES
?
我的意思是,例如,我想将一个现有函数限制为动态创建的角色 ROLE_NEWROLE
,因此在基本代码(原始来源)中不存在,那么我该如何限制该新角色的现有功能?以文档here为例:
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
class PostController extends Controller
{
/**
* @Security("has_role('ROLE_ADMIN')")
*/
public function indexAction()
{
// ...
}
}
上面的代码假定 ROLE_ADMIN
已经在某处以某种方式声明,但是如果我想通过安全组件将新的 ROLE_NEWROLE
添加到该函数怎么办?我需要一直触摸我的代码吗?这一点都不好笑,所以我想知道你对这个话题的看法。
当我们谈到这个 时,您需要实施 EventListener
来听取您的 onKernelRequest
。
用简单的英语来说,这意味着 所有 的控制器操作将首先执行 onKernelRequest
,然后才能访问原始控制器。所以这样你就不必写
/**
* @Security("has_role('ROLE_ADMIN')")
*/
在每个控制器动作中。
现在,您可以决定在此方法中执行的操作。我的方法是制作一个 table,它将 ROLE
与 ROUTE
相关联。这个 table 会比较大,因为您必须包含所有 ROLES
才能访问所有 ROUTES
。
table 结构可以是这样的:
ACCESSID ROLENAME ROUTENAME
1 ROLE_NEWUSER contacts_lookup_homepage
2 ROLE_SUPER_USER contacts_lookup_homepage
根据此 table 只有 ROLE_NEWUSER
和 ROLE_SUPER_USER
有资格访问路线 contacts_lookup_homepage
现在只有那些角色才允许访问 contacts_lookup_homepage
路由。现在在 onKernelRequest
上,您要做的就是查询此 table 并检查是否存在与该路由的该角色的匹配项。您可以使用此方法访问两者。这些路线与您在每个路线的 routing.yml
文件中定义的路线相同。如果您不确定,它看起来像这样:
contacts_lookup_homepage:
path: /Contacts/Lookup
defaults: { _controller: ContactsLookupBundle:Default:index }
现在终于可以在 onKernelRequest
中执行以下操作了:
public function onKernelRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
$route = $request->attributes->get('_route');
$routeArr = array('fos_js_routing_js', 'fos_user_security_login', '_wdt'); //These are excluded routes. These are always allowed. Required for login page
$roleArr = $this->token_storage->getToken()->getUser()->getRoles();
if(!is_int(array_search($route, $routeArr))) //This is for excluding routes that you don't want to check for.
{
//Check for a matching role and route
$qb = $this->em->getRepository('AppBundle:UserAccess')->createQueryBuilder('o');
$qb
->select('o')
->where('o.ROLENAME IN (:roleArr)')
->setParameter('roleArr', $roleArr)
->andWhere('o.ROUTENAME = :route')
->setParameter('route', $route)
;
$result = $qb->getQuery()->getArrayResult();
if(empty($result))
{
//A matching role and route was not found so we do not give access to the user here and redirect to another page.
$event->setResponse(new RedirectResponse($this->router->generate('user_management_unauthorized_user', array())));
}
}
}
services.yml
可以这样:
services:
app.tokens.action_listener:
class: EventListenerBundle\EventListener\TokenListener
arguments:
entityManager: "@doctrine.orm.entity_manager"
token_storage: "@security.token_storage"
templating: "@templating"
router: "@router"
resolver: "@controller_resolver"
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
这将保证没有未经授权的用户访问未经授权的控制器操作。我希望这能让您对实施有所了解。
这个 post 旨在成为 Many To Many
在 roles
和 users
之间 我可能会问:
- 我有动态角色,现在如何使用这些新角色
ROLES
?
我的意思是,例如,我想将一个现有函数限制为动态创建的角色 ROLE_NEWROLE
,因此在基本代码(原始来源)中不存在,那么我该如何限制该新角色的现有功能?以文档here为例:
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
class PostController extends Controller
{
/**
* @Security("has_role('ROLE_ADMIN')")
*/
public function indexAction()
{
// ...
}
}
上面的代码假定 ROLE_ADMIN
已经在某处以某种方式声明,但是如果我想通过安全组件将新的 ROLE_NEWROLE
添加到该函数怎么办?我需要一直触摸我的代码吗?这一点都不好笑,所以我想知道你对这个话题的看法。
当我们谈到这个 EventListener
来听取您的 onKernelRequest
。
用简单的英语来说,这意味着 所有 的控制器操作将首先执行 onKernelRequest
,然后才能访问原始控制器。所以这样你就不必写
/**
* @Security("has_role('ROLE_ADMIN')")
*/
在每个控制器动作中。
现在,您可以决定在此方法中执行的操作。我的方法是制作一个 table,它将 ROLE
与 ROUTE
相关联。这个 table 会比较大,因为您必须包含所有 ROLES
才能访问所有 ROUTES
。
table 结构可以是这样的:
ACCESSID ROLENAME ROUTENAME
1 ROLE_NEWUSER contacts_lookup_homepage
2 ROLE_SUPER_USER contacts_lookup_homepage
根据此 table 只有 ROLE_NEWUSER
和 ROLE_SUPER_USER
有资格访问路线 contacts_lookup_homepage
现在只有那些角色才允许访问 contacts_lookup_homepage
路由。现在在 onKernelRequest
上,您要做的就是查询此 table 并检查是否存在与该路由的该角色的匹配项。您可以使用此方法访问两者。这些路线与您在每个路线的 routing.yml
文件中定义的路线相同。如果您不确定,它看起来像这样:
contacts_lookup_homepage:
path: /Contacts/Lookup
defaults: { _controller: ContactsLookupBundle:Default:index }
现在终于可以在 onKernelRequest
中执行以下操作了:
public function onKernelRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
$route = $request->attributes->get('_route');
$routeArr = array('fos_js_routing_js', 'fos_user_security_login', '_wdt'); //These are excluded routes. These are always allowed. Required for login page
$roleArr = $this->token_storage->getToken()->getUser()->getRoles();
if(!is_int(array_search($route, $routeArr))) //This is for excluding routes that you don't want to check for.
{
//Check for a matching role and route
$qb = $this->em->getRepository('AppBundle:UserAccess')->createQueryBuilder('o');
$qb
->select('o')
->where('o.ROLENAME IN (:roleArr)')
->setParameter('roleArr', $roleArr)
->andWhere('o.ROUTENAME = :route')
->setParameter('route', $route)
;
$result = $qb->getQuery()->getArrayResult();
if(empty($result))
{
//A matching role and route was not found so we do not give access to the user here and redirect to another page.
$event->setResponse(new RedirectResponse($this->router->generate('user_management_unauthorized_user', array())));
}
}
}
services.yml
可以这样:
services:
app.tokens.action_listener:
class: EventListenerBundle\EventListener\TokenListener
arguments:
entityManager: "@doctrine.orm.entity_manager"
token_storage: "@security.token_storage"
templating: "@templating"
router: "@router"
resolver: "@controller_resolver"
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
这将保证没有未经授权的用户访问未经授权的控制器操作。我希望这能让您对实施有所了解。