Symfony 4 - 从数据库定义自定义用户角色

Posted

技术标签:

【中文标题】Symfony 4 - 从数据库定义自定义用户角色【英文标题】:Symfony 4 - Define custom user roles from DB 【发布时间】:2018-07-27 16:33:24 【问题描述】:

我正在学习 Symfony 4,但我迷失了用户角色,不知道从哪里开始。 因为它将是一个类似网站的内网用户不会自己注册,管理员将注册他们并设置角色。管理员还可以动态创建新角色。 我想将用户角色存储在数据库中。我有 2 个实体 UserRole。 (关系还没有确定,这很正常)

这是我的User 实体:

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\UserInterface;

/**
 * @ORM\Entity(repositoryClass="App\Repository\UserRepository")
 * @UniqueEntity(fields="email", message="Email already taken")
 * @UniqueEntity(fields="username", message="Username already taken")
 */
class User implements UserInterface, \Serializable

    /**
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     * @ORM\Column(type="integer", unique=true)
     */
    private $id;

    /**
    * @ORM\Column(type="string", length=255)
    */
    private $firstname;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $lastname;

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

    /**
     * @ORM\Column(type="date")
     */
    private $birthdate;

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

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

    /**
     * @Assert\NotBlank()
     * @Assert\Length(max=4096)
     */
    private $plainPassword;

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

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

    // GETTERS

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

    public function getFirstname()
    
        return $this->firstname;
    

    public function getLastname()
    
        return $this->lastname;
    

    public function getBirthdate()
    
        return $this->birthdate;
    

    public function getEmail()
    
        return $this->email;
    

    public function getRoleId()
    
        return $this->roleId;
    

    public function getRoles()
    
        return array('ROLE_USER');
    

    public function getUsername()
    
      return $this->username;
    

    public function getPlainPassword()
    
        return $this->plainPassword;
    

    public function getPassword()
    
      return $this->password;
    

    public function getSalt()
    
     return null;
    

    // SETTERS

    public function setFirstname($firstname)
    
        $this->firstname = $firstname;
    

    public function setLastname($lastname)
    
        $this->lastname = $lastname;
    

    public function setEmail($email)
    
        $this->email = $email;
    

    public function setBirthdate($birthdate)
    
        $this->birthdate = $birthdate;
    

    public function setRoleId($roleId)
    
        $this->roleId = $roleId;
    

    public function setUsername($username)
    
        $this->username = $username;
    

    public function setPlainPassword($password)
    
        $this->plainPassword = $password;
    


    public function setPassword($password)
    
        $this->password = $password;
    

    // FUNCTIONS

    public function serialize()
    
        return serialize(array(
            $this->id,
            $this->username,
            $this->password,
            // see section on salt below
            // $this->salt,
        ));
    

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

    public function eraseCredentials()
    

    

这是我的Role 实体:

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass="App\Repository\RoleRepository")
 */
class Role

    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer", unique=true)
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=255, nullable=false)
     */
    private $type;

    // GETTERS

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

    public function getType()
    
        return $this->type;
    

    // SETTERS

    public function setType($type): void
    
        $this->type = $type;
    

这样做是个好习惯吗??

在 Symfony 文档中,我们到处都可以看到“宏”ROLE_USERROLE_ADMIN... 它们是在哪里定义的?我们可以自定义这个吗?

【问题讨论】:

【参考方案1】:

Symfony 过去使用 RoleInterface 支持角色实体,就像您的用户实现 UserInterface 一样。决定这是不必要的,因为角色表通常只包含 2 个字段(id、名称),这意味着我们不妨直接将角色存储在用户表中。

因此,如果您想遵循 Symfony 最佳实践,您只需在用户中拥有您的角色,例如:

class User implements UserInterface

    /** @Column(type="json") */
    private $roles = [];

    public function getRoles(): array
    
        return array_unique(array_merge(['ROLE_USER'], $this->roles));
    

    public function setRoles(array $roles)
    
        $this->roles = $roles;
    

    public function resetRoles()
    
        $this->roles = [];
    

如果您不想将角色存储为 JSON 编码的字符串,您也可以使用 @Column(type="array") */ 或编写自定义 DBAL 类型,例如某种 EnumType。您可能还希望保护 setter 免受无效数据的影响或添加验证。

关于文档中使用的角色的第二个问题:Symfony 有一些预定义的角色和某些功能的伪角色:

IS_AUTHENTICATED_ANONYMOUSLY IS_AUTHENTICATED_REMEMBERED IS_AUTHENTICATED_FULLY ROLE_ALLOWED_TO_SWITCH(用于用户切换)

ROLE_USERROLE_ADMIN 等其他角色纯粹是示范性的,您可以根据需要使用它们。您甚至不必以 ROLE_ 开始您自己的角色(尽管某些安全功能依赖于此约定,并且除非您进行一些手动更改,否则这些角色将无法按预期工作)。

【讨论】:

【参考方案2】:

当前 Symfony 文档的推荐读物(本回答时的版本 4): https://symfony.com/doc/current/security.html#hierarchical-roles

Symfony 建议定义角色继承,看看是否有帮助;从上面的链接,

" 您可以通过创建角色层次结构来定义角色继承规则,而不是为每个用户分配多个角色:

# config/packages/security.yaml
security:
# ...

role_hierarchy:
    ROLE_ADMIN:       ROLE_USER
    ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]

具有 ROLE_ADMIN 角色的用户也将具有 ROLE_USER 角色。而拥有 ROLE_SUPER_ADMIN 的用户,将自动拥有 ROLE_ADMIN、ROLE_ALLOWED_TO_SWITCH 和 ROLE_USER(继承自 ROLE_ADMIN)。

要使角色层次结构起作用,不要尝试手动调用 $user->getRoles()。例如,在从基本控制器扩展的控制器中:

// BAD - $user->getRoles() will not know about the role hierarchy
$hasAccess = in_array('ROLE_ADMIN', $user->getRoles());

// GOOD - use of the normal security methods
$hasAccess = $this->isGranted('ROLE_ADMIN');
$this->denyAccessUnlessGranted('ROLE_ADMIN');

" 复制结束

如果继承符合您的需要,我认为将角色存储为字符串就足够了。

【讨论】:

以上是关于Symfony 4 - 从数据库定义自定义用户角色的主要内容,如果未能解决你的问题,请参考以下文章

Symfony 忽略 fosuser 自定义角色

Symfony2:如何实现自定义用户登录和注册——摆脱 FOSUSerBundle

Symfony 4 - 自定义 KnpPaginator 分页模板?

Symfony 1.4:表单中 CSRF 的自定义错误消息

如何使用自定义函数 Twig-symfony

Symfony 4,从自定义类(不是控制器类)获取项目的根路径