简体   繁体   English

如何动态处理Symfony2中的角色|权限:使用动态角色限制功能

[英]How to dynamic handle roles|permissions in Symfony2: restrict functions with dynamic roles

This post aims to be a kind of second part of this 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 useful UserBundle which help me as an example to illustrate possible relationship as Many To Many between roles and users I may ask: 拥有该信息和上一篇文章的答案,并且还发现了这个有用的UserBundle ,可以帮助我举例说明rolesusers之间可能存在的Many To Many关系:

  • I have dynamic roles and now how I use those new ROLES ? 我拥有动态角色,现在如何使用这些新ROLES

What I mean, for example I want to limit a existent function to role ROLE_NEWROLE which has been created dynamically and therefore doesn't exists on the base code (original sources) then how do I restrict a existent function to that new role? 我的意思是,例如,我想将现有功能限制为已动态创建的ROLE_NEWROLE角色,因此在基础代码(原始源代码)上不存在,那么如何将现有功能限制为该新角色? Take the docs here as an example: 此处的文档为例:

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;

class PostController extends Controller
{
    /**
     * @Security("has_role('ROLE_ADMIN')")
     */
    public function indexAction()
    {
        // ...
    }
}

The code above presume that ROLE_ADMIN is already declared somewhere and somehow but what if I want to add the new ROLE_NEWROLE to that function through security component? 上面的代码假定ROLE_ADMIN已经在某处以某种方式声明,但是如果我想通过安全组件将新的ROLE_NEWROLE添加到该函数中怎么办? Do I need to touch my code all the time? 我是否需要一直触摸我的代码? That isn't funny at all so I want to know your opinions about this topic. 这一点都不好笑,所以我想知道您对这个话题的看法。

As we talked about this before , you need to implement EventListener which will listen to your onKernelRequest . 正如我们之前讨论的那样,您需要实现EventListener ,它将侦听onKernelRequest

In plain English, this means that all of your Controller Actions will execute onKernelRequest first before giving access to the original controller. 用简单的英语来说,这意味着在授予对原始控制器的访问权限之前, 所有控制器动作将首先执行onKernelRequest So this way you won't have to write 这样您就不必写

/**
* @Security("has_role('ROLE_ADMIN')")
*/

in every controller action. 在每个控制器动作中。

Now, its upto you what you want to do in this method. 现在,取决于您要使用此方法执行的操作。 My approach was to make a table which associates a ROLE with a ROUTE . 我的方法是制作一个将ROLEROUTE关联的表。 This table will be comparatively big because you have to include all ROLES you want to give access to all ROUTES . 该表将相对较大,因为您必须包括要授予所有ROUTES访问权限的所有ROLES

The table structure can be something like this: 表结构可以是这样的:

ACCESSID      ROLENAME                ROUTENAME
    1       ROLE_NEWUSER       contacts_lookup_homepage
    2       ROLE_SUPER_USER    contacts_lookup_homepage

According to this table only ROLE_NEWUSER and ROLE_SUPER_USER are eligible to access the route contacts_lookup_homepage 根据此表,只有ROLE_NEWUSERROLE_SUPER_USER才有资格访问路线contacts_lookup_homepage

This way now only those roles are allowed to access contacts_lookup_homepage route. 现在,仅允许这些角色访问contacts_lookup_homepage路由。 Now on the onKernelRequest all you will do is query this table and check if there is a match with that role with that route. 现在,在onKernelRequest上,您要做的就是查询该表并检查该角色与该路由是否匹配。 You have access to both in this method. 您可以使用此方法访问两者。 These routes are the same as the one you define in your routing.yml file of every route. 这些路由与您在每条路由的routing.yml文件中定义的路由相同。 If you're not sure, it looks something like this: 如果不确定,它看起来像这样:

contacts_lookup_homepage:
    path:     /Contacts/Lookup
    defaults: { _controller: ContactsLookupBundle:Default:index }

Now finally in your onKernelRequest you can do something like this: 现在终于可以在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())));
        }
    }
}

The services.yml can be like this: 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 }

This will guarantee that no unauthorized user accesses the controller action which is not authorized. 这将确保没有未经授权的用户访问未经授权的控制器操作。 I hope that gives you an idea about the implementation. 希望您能对实现有所了解。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM