Symfony 4 Doctrine,如何从数据库加载用户角色

Symfony 4 Doctrine, How to load user Roles from the Database

Symfony 的新手。如何使用 Doctrine 从数据库中加载当前登录用户的角色。我有 3 个 table 像这样布置。

users => (user_id, username, password, email)

user_roles => (id,user_id,role_id)

roles => (role_id, role_name)

每个 table 我都有实体及其相应的存储库。

我的 security.yaml 看起来像这样。

security:
encoders:
    App\Entity\User:
        algorithm: bcrypt

providers:
    our_db_provider:
        entity:
            class: App\Entity\User
            property: username
            # if you're using multiple entity managers
            # manager_name: customer

firewalls:
    dev:
        pattern: ^/(_(profiler|wdt)|css|images|js)/
        security: false
    main:
#            pattern: ^/
#            http_basic: ~
        provider: our_db_provider
        anonymous: true
        form_login:
            #login_path is GET request used ti display the form
            # needs to be route names(alias) not the path.
            login_path: login

            #check_path is a POST request
            check_path: logincheck
            use_forward: true

            default_target_path: default
            always_use_default_target_path: true

我的 Entity/User 实现了 UserInterface 组件,通过阅读文档我了解到 getRoles() 方法负责更新用户角色。我在我的 UserRolesRepository.php 中创建了一个名为 getUserRoles($id) 的自定义方法,我设法 return 当前用户角色的字符串数组但是我无法从 Entity.我知道我不应该从实体 class 访问存储库方法,但我深深地困在这个阶段。所以现在我的 getRoles() 方法 returns 静态数组 return array('ROLE_ADMIN', 'ROLE_EDITOR');

我的 User 实体 Class

namespace App\Entity;

use App\Repository\UserRepository;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
use App\Repository\UserRolesRepository;
use Doctrine\ORM\EntityRepository;
use App\Services\Helper;

/**
 * @ORM\Table(name="`user`")
 * @ORM\Entity(repositoryClass="App\Repository\UserRepository")
 */
class User implements UserInterface, \Serializable
{
    /**
    * @ORM\Id()
    * @ORM\GeneratedValue()
    * @ORM\Column(type="integer")
    */
private $id;

/**
 * @ORM\Column(type="string", length=25, nullable=true)
 */
private $username;

/**
 * @ORM\Column(type="string", length=64, nullable=true)
 */
private $password;

/**
 * @ORM\Column(type="string", length=254, nullable=true)
 */
private $email;

/**
 * @ORM\Column(type="boolean", nullable=true)
 */
private $isActive;

private $roles;

/**
 * @ORM\Column(type="string", length=254, nullable=true)
 */
private $role;

public function __construct()
{
    $this->isActive = true;
}

/**
 * @return mixed
 */
public function getRole()
{
    return $this->role;
}

/**
 * @param mixed $role
 */
public function setRole($role)
{
    $this->role = $role;
}

public function getId()
{
    return $this->id;
}

public function getUsername(): ?string
{
    return $this->username;
}

public function setUsername(?string $username): self
{
    $this->username = $username;

    return $this;
}

public function getPassword(): ?string
{
    return $this->password;
}

public function setPassword(?string $password): self
{
    $this->password = $password;

    return $this;
}

public function getEmail(): ?string
{
    return $this->email;
}

public function setEmail(?string $email): self
{
    $this->email = $email;

    return $this;
}

public function getIsActive(): ?bool
{
    return $this->isActive;
}

public function setIsActive(?bool $isActive): self
{
    $this->isActive = $isActive;

    return $this;
}


//return is required or else returns an fatal error.
public function getRoles()
{
    return array('ROLE_ADMIN','ROLE_EDITOR');
}

public function eraseCredentials()
{
    // TODO: Implement eraseCredentials() method.
}

public function serialize()
{
    // TODO: Implement serialize() method.
    return serialize(array(
        $this->id,
        $this->username,
        $this->password,
    ));
}

/** @see \Serializable::unserialize() */
public function unserialize($serialized)
{
    list (
        $this->id,
        $this->username,
        $this->password,
        // see section on salt below
        // $this->salt
        ) = unserialize($serialized, ['allowed_classes' => false]);
}

public function getSalt()
{
    // TODO: Implement getSalt() method.
    return null;
}
}

Symfony 角色与安全选民密切合作。如果你改变在 Symfony 中进行角色管理的标准方式(不使用 ROLE_SOMETHING),那么你应该做三件事:

  1. getRoles getter 映射到 return 用户 class 中的角色对象集合。使用学说,您可以轻松地将关系映射到 ManyToMany。

  2. 然后你必须创建一个 GuardAuthenticator 并覆盖 createAuthenticatedToken 方法,所以你将你制作的自定义角色传递给 Symfony 的 Auth Token(这是 class 负责应用程序中的访问控制)。

  3. 你必须实现一个安全投票器,它可以从数据库中获取角色,并投票决定用户是否可以做某事。

如您所见,这一切都非常复杂,但并非不可能。我认为 Symfony 的内置角色系统足以满足您的需求。

到目前为止,您还没有根据数据库结构将用户映射到角色。

private $roles;

没有关于它如何映射到角色 table 的信息。它应该看起来像:

/**
 * @var Collection|Role[]
 * @ORM\ManyToMany(targetEntity="Role")
 * @ORM\JoinTable(
 *      name="user_roles",
 *      joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
 *      inverseJoinColumns={@ORM\JoinColumn(name="role_id", referencedColumnName="id")}
 * )
 */
private $roles;

您还需要在构造中创建一组初始角色,这样 getRoles 就不会引发错误,并且可以在需要时将角色一个一个地添加到新用户:

public function __construct()
{
    $this->isActive = true;
    $this->roles = new ArrayCollection();
}

你可以删除getRole()setRole(),因为我们没有一个单一的角色(除非接口要求),你可以失去当前的$role 属性:

/**
 * @ORM\Column(type="string", length=254, nullable=true)
 */
private $role;

但添加一个 setter 以收集:

public function setRoles(Collection $roles)
{
    $this->roles = $roles;
}

然后获取角色:

public function getRoles()
{
    return $this->roles->toArray();
}

如果您使用表单创建用户(尤其是 Sonata Admin 表单),除了添加和删除用户的单个角色外,您还可以使用以下方法(它将删除用户和用户之间的关系)角色,而不是角色本身):

public function addRole(Role $role)
{
    $this->roles->add($role);
}

还有一个删除角色:

public function removeRole(Role $role)
{
    $this->roles->removeElement($role);
}

这是在 Symfony 4 中将 2 个表与多个用户相关联的解决方案

//Entity Usuarios
/**
     * @var \Role
     *
     * @ORM\ManyToOne(targetEntity="Role")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="Role_id", referencedColumnName="id")
     * })
     */
    private $role;

这就是方法getRoles()

public function getRoles()
{
   //return array('ROLE_USER');
   return array($this->role->getRol());
}

这样数组returns登录用户的角色

具有 3 个 table(用户/角色/user_roles)的场景非常普遍,应该在手册中记录。

在我的例子中,为了让它工作,我应用了“确定”的答案,然后我遇到了“Vasiliy Toporov”和“Radu”发出的问题。 $this->roles->toArray() 在 getRoles() 中是不够的,因为它 returns 一个 Role 实体数组,而不是预期的字符串数组(Symfony\Component\Security\Core\Authentication\Token\AbstractToken 预期)。

为了让它工作,我首先在 Role 实体中添加 class(rlCode = 字符串代码;ROLE_ADMIN 等):

public function __toString(): string
{
    return $this->rlCode;
}

然后,在用户实体 class 中,我将 getRoles() 更改为:

public function getRoles()
{   
    $arrRolesString = [];

    foreach($this->roles->getIterator() as $i => $item) {
        $arrRolesString[] = (string)$item;
    }
    return $arrRolesString;        
}

现在可以了。然而,我遇到的下一个问题是分配给用户的所有多个角色都是连接查询检索到的第一个角色的副本,我不知道为什么(连接查询 returns 所有角色正确,但这一定是某处 ManyToMany 分配的问题...如果有人知道,请告诉)

编辑:请忽略重复问题。这是因为 Doctrine 将实体映射 tinyint(我在 Roles table 中的 id 列是 tinyint)作为布尔值,而不是整数。