【问题标题】:Is granted for other user授予其他用户
【发布时间】:2021-03-27 00:31:51
【问题描述】:

在我阅读了食谱中的这一章之后

http://symfony.com/doc/current/cookbook/security/entity_provider.html

我创建了一个实现“AdvancedUserInterface”的实体“User”和一个实现“RoleInterface”的实体“Roles”。我还在我的“security.yml”中创建了一个角色结构。

用户和角色之间的关系是“多对多”关系。

一切都很好。

对于已登录的用户,我可以像这样检查授权:

$this->get('security.context')->isGranted("ROLE_EDITOR");

但是我怎样才能检查数据库中其他用户的这个授权呢?

有类似的吗?

$this->get('security.context')->isGranted("ROLE_EDITOR", $user);

【问题讨论】:

标签: symfony


【解决方案1】:

Symfony 5 答案:

namespace App\Controller;

...
use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;
use Symfony\Component\Security\Core\Role\RoleHierarchy;

class UserController extends AbstractController
{
    private $roleHierarchy;

    /**
     * @Route("/users", name="users")
     */
    public function usersIndex(RoleHierarchyInterface $roleHierarchy)
    {
        $this->roleHierarchy = $roleHierarchy;

        // your user service or your Doctrine code here
        $users = ...

        foreach ($users as $user) {
            $roles = $roleHierarchy->getReachableRoleNames($user->getRoles());
            \dump($roles);

            if ($this->isGranted($user, 'ROLE_SUPER_ADMIN')) {
                ...
            }
        }
        ...
    }

    private function isGranted(User $user, string $role): bool 
    {
        $reachableRoles = $this->roleHierarchy->getReachableRoleNames($user->getRoles());

        foreach ($reachableRoles as $reachableRole) {
            if ($reachableRole === $role) {
                return true;
            }
        }

        return false;
    }
}

注意:为了简单起见,我将所有内容都放在了控制器中,但我当然建议将角色管理代码移动到单独的服务中,例如 @leberknecht 的回答。

【讨论】:

    【解决方案2】:

    不知道现在是否有内置的东西,但构建起来非常简单:

    class RoleCheckerService
    {
        private const PROVIDER_KEY = 'role-check';
        /**
         * @var RoleHierarchyInterface
         */
        private $roleHierarchy;
    
        public function __construct(RoleHierarchyInterface $roleHierarchy)
        {
            $this->roleHierarchy = $roleHierarchy;
        }
    
        final public function isGranted(User $user, string $role): bool 
        {
            $token = new PreAuthenticatedToken($user, null, self::PROVIDER_KEY, $user->getRoles());
            $reachableRoles = $this->roleHierarchy->getReachableRoles($token->getRoles());
    
            foreach ($reachableRoles as $reachableRole) {
                if ($reachableRole->getRole() === $role) {
                    return true;
                }
            }
    
            return false;
        }
    }
    

    【讨论】:

    • 为什么是$token = new PreAuthenticatedToken($user, null, self::PROVIDER_KEY, $user->getRoles()); inatead of $user->getRoles()
    • @ErdalG。您可以在安全配置中设置 role_hierarchy。示例:我的应用程序中的 ROLE_SUPER_ADMIN 将所有其他角色授予用户。因此,您需要调用 RoleHierarchy::getReachableRoles 来确定用户具有哪些角色。 symfony.com/doc/current/security.html#hierarchical-roles
    • @Skylord123 我明白了,我说的是:preAuth stuff + $token->getRoles() 而不是更简单的$security->getUser()->getRoles()。但是不知道有没有区别
    • 实际上$token->getRoles() 甚至不是一个函数,而且该代码是错误的(至少在 Symfony 5.X 中没有)。应该是$user->getRoles()。虽然有$token->getRoleNames(),但基本上只是返回传递给令牌的相同角色。也许最好从令牌中删除角色以防万一那里有一些修改它们的逻辑?这是我的猜测。
    【解决方案3】:

    在 PHP 代码中,我只注入 RoleHierarchyInterface 并使用这个简单的表达式:

    if (in_array($role, $this->roleHierarchy->getReachableRoleNames($user->getRoles()))) {
      // whatever
    }
    

    在 Twig 中,我编写了这个简单的扩展(适用于当前版本:Symfony 5.2 和 Twig 3.3):

    <?php
    // /src/Twig/AccessCheckExtension.php
    declare(strict_types=1);
    namespace App\Twig;
    
    use Twig\Extension\AbstractExtension;
    use Twig\TwigFilter;
    use Twig\TwigFunction;
    
    class AccessCheckExtension extends AbstractExtension
    {
        public function getFilters(): array
        {
            return [
                new TwigFilter('has_role', [AccessCheckRuntime::class, 'hasRole'])
            ];
        }
        
        public function getFunctions(): array
        {
            return [
                new TwigFunction('has_role', [AccessCheckRuntime::class, 'hasRole'])
            ];
        }
    }
    

    <?php
    // /src/Twig/AccessCheckRuntime.php
    declare(strict_types=1);
    namespace App\Twig;
    
    use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;
    use Symfony\Component\Security\Core\User\UserInterface;
    use Twig\Extension\RuntimeExtensionInterface;
    
    class AccessCheckRuntime implements RuntimeExtensionInterface
    {
        /**
         * this uses PHP 8.0 constructor property promotion to reduce the needed lines
         **/
        public function __construct(
            protected RoleHierarchyInterface $roleHierarchy
        ){}
    
        public function hasRole(UserInterface $user, string $role): bool
        {
            return in_array($role, $this->roleHierarchy->getReachableRoleNames($user->getRoles()));
        }
    }
    

    这可以在 Twig 模板中使用,如下所示:

    {% if user|has_role('ROLE_WHATEVER') %}
    Hello {{ user.name }}
    {% endif %}
    
    or
    
    {% if has_role(user, 'ROLE_WHATEVER') %}
    Hello {{ user.name }}
    {% endif %}
    

    非常简单。我更喜欢过滤器变体。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-11-08
      • 2018-11-19
      • 1970-01-01
      • 1970-01-01
      • 2019-10-03
      • 2013-09-14
      • 2019-12-12
      • 2013-10-14
      相关资源
      最近更新 更多