如何在 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 Manyrolesusers 之间 我可能会问:

我的意思是,例如,我想将一个现有函数限制为动态创建的角色 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,它将 ROLEROUTE 相关联。这个 table 会比较大,因为您必须包含所有 ROLES 才能访问所有 ROUTES

table 结构可以是这样的:

ACCESSID      ROLENAME                ROUTENAME
    1       ROLE_NEWUSER       contacts_lookup_homepage
    2       ROLE_SUPER_USER    contacts_lookup_homepage

根据此 table 只有 ROLE_NEWUSERROLE_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 }

这将保证没有未经授权的用户访问未经授权的控制器操作。我希望这能让您对实施有所了解。