【问题标题】:Symfony 2 - Loading roles from databaseSymfony 2 - 从数据库加载角色
【发布时间】:2014-01-08 20:20:38
【问题描述】:

我的角色存储在数据库中,我尝试在登录时动态加载它们。我正在做的是查询角色并将它们设置在我的用户提供程序中的用户对象上,如下所示:

public function loadUserByUsername($username) {
    $q = $this
        ->createQueryBuilder('u')
        ->where('u.username = :username')
        ->setParameter('username', $username)
        ->getQuery()
    ;

    try {
        // The Query::getSingleResult() method throws an exception
        // if there is no record matching the criteria.
        $user = $q->getSingleResult();

        // Permissions
        $permissions = $this->_em->getRepository('E:ModulePermission')->getPermissionsForUser($user);

        if ($permissions !== null) {
            foreach ($permissions as $permission) {
                $name = strtoupper(str_replace(" ", "_", $permission['name']));

                $role = "ROLE_%s_%s";

                if ($permission['view']) {
                    $user->addRole(sprintf($role, $name, 'VIEW'));
                }

                if ($permission['add']) {
                    $user->addRole(sprintf($role, $name, 'ADD'));
                }

                if ($permission['edit']) {
                    $user->addRole(sprintf($role, $name, 'EDIT'));
                }

                if ($permission['delete']) {
                    $user->addRole(sprintf($role, $name, 'DELETE'));
                }
            }
        }

    } catch (NoResultException $e) {
        throw new UsernameNotFoundException(sprintf('Unable to find an active admin Entity:User object identified by "%s".', $username), null, 0, $e);
    }

    return $user;
}

还有用户实体:

class User implements AdvancedUserInterface, \Serializable {
    ....

    protected $roles;

    ....

    public function __construct() {
        $this->salt = base_convert(sha1(uniqid(mt_rand(), true)), 16, 36);
        $this->roles = array();
    }


    ....


    public function getRoles() {
        $roles = $this->roles;

        // Ensure we having something
        $roles[] = static::ROLE_DEFAULT;

        return array_unique($roles);
    }

    public function addRole($role) {
        $role = strtoupper($role);
        $roles = $this->getRoles();
        if ($role === static::ROLE_DEFAULT) {
            return $this;
        }

        if (!in_array($role, $roles, true)) {
            $this->roles[] = $role;
        }

        return $this;
    }

    public function hasRole($role) {
        $role = strtoupper($role);
        $roles = $this->getRoles();
        return in_array($role, $roles, true);
    }
}

这很好用,而且我看到了正确的角色:

$this->get('security.context')->getUser()->getRoles()

问题(我认为)是令牌不知道这些角色。因为在令牌上调用 getRoles() 只显示 ROLE_USER,这是默认角色。

在我看来,令牌是在 UserProvider 加载用户之前创建的。我已经查看了很多安全组件,但我一生都无法找到正确的流程部分来正确设置这些角色,以便令牌了解它们。

更新遵循从数据库文档加载角色可以正常工作,但这与我的用例不匹配,如此处所示。我的架构不同,因为每个角色都有额外的权限(查看/添加/编辑/删除),这就是我在这里尝试这种方法的原因。我不想仅仅为了使用 Symfony 的安全性而改变我的模式。我宁愿理解为什么这些角色此时没有正确绑定到我的用户对象上(这里不确定正确的教义词)。

【问题讨论】:

    标签: php security symfony


    【解决方案1】:

    您可能不知道 Symfony 提供的内置角色管理。阅读文档 - Managing roles in the database 做你想做的事情其实很简单,你需要做的就是实现一个接口并定义你需要的函数。我链接的文档提供了很好的示例。看看吧。

    更新

    看起来文档没有为您提供 AdvancedUserInterface 的 use 语句。这里是:

    // in your user entity
    use Symfony\Component\Security\Core\User\AdvancedUserInterface;
    

    然后在你的角色实体中:

    use Symfony\Component\Security\Core\Role\RoleInterface;
    

    文档向您展示了如何完成剩下的工作。

    更新

    看看这篇博文,它展示了如何动态创建角色:

    Dynamically create roles

    【讨论】:

    • 我的实体已经扩展了 AdvancedUserInterface,如您所见,我已经在我的用户对象上设置了角色。从我所看到的情况来看,我正在按照文档的建议进行操作(实际上我已经阅读了它们)。更新了用户实体的相关部分。
    • 您受保护的roles 变量应该是ArrayCollection 类型,因此在您的getRoles 函数中,您正在重新定义$roles 变量,使其成为一个新数组,只有一个元素( ROLE_USER 刚刚分配给它)。 The doctrine docs 向您展示了您可以使用数组集合做什么,您可能想尝试 toArray() 函数,它将数组集合转换为 php 数组。
    • 另外,你的 Role 类是否实现了RoleInterface?您能否也包括您的角色实体?
    • 来自文档“注意 Role 类实现了 RoleInterface。这是因为 Symfony 的安全系统要求 User::getRoles 方法返回一个由角色字符串或实现此接口的对象组成的数组。”。我正在返回一个字符串数组,所以这无关紧要。
    • 您忽略了刚才引用的同一段:“如果 Role 没有实现此接口,则 User::getRoles 将需要遍历所有 Role 对象,在每个对象上调用 getRole,并且创建要返回的字符串数组。”看起来您并没有这样做,那么为什么不实现 RoleInterface?
    【解决方案2】:

    这里的问题源于我认为我正在实施的事实

    Symfony\Component\Security\Core\User\EquatableInterface;
    

    但不是(如您在原始问题中所见,我忘记将其添加到我的类定义中)。如果人们遇到它,我会把它留在这里。您只需要实现此接口,并将以下方法添加到您的用户实体。

    public function isEqualTo(UserInterface $user) {
        if ($user instanceof User) {
        // Check that the roles are the same, in any order
            $isEqual = count($this->getRoles()) == count($user->getRoles());
            if ($isEqual) {
                foreach($this->getRoles() as $role) {
                    $isEqual = $isEqual && in_array($role, $user->getRoles());
                }
            }
            return $isEqual;
        }
    
        return false;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-05-04
      • 1970-01-01
      • 1970-01-01
      • 2012-10-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-26
      相关资源
      最近更新 更多