Php 在Symfony 4中是否将身份验证与用户实体分离?

Php 在Symfony 4中是否将身份验证与用户实体分离?,php,doctrine,symfony4,Php,Doctrine,Symfony4,在使用同一个实体来维护员工和跟踪我的网站登录时遇到一些问题后,我发现了一篇关于解除安全用户的连接的帖子。不幸的是,我觉得这篇文章在如何完全实现它方面遗漏了很多内容(似乎我不是唯一一个)。首先,我为什么需要这样做,以及如何在Symfony 4中实现它?我为什么需要这样做? 这将得到更详细的解释 …这也会带来副作用: 您将在开发人员倾向于使用的会话中使用此实体 如果最终的结果是 在会话中,您将遇到同步问题。如果你 更新您的实体,这意味着您的会话实体将不会更新为 不是数据库里的。为了解决这个问题,您可

在使用同一个实体来维护员工和跟踪我的网站登录时遇到一些问题后,我发现了一篇关于解除安全用户的连接的帖子。不幸的是,我觉得这篇文章在如何完全实现它方面遗漏了很多内容(似乎我不是唯一一个)。首先,我为什么需要这样做,以及如何在Symfony 4中实现它?

我为什么需要这样做? 这将得到更详细的解释

…这也会带来副作用: 您将在开发人员倾向于使用的会话中使用此实体 如果最终的结果是 在会话中,您将遇到同步问题。如果你 更新您的实体,这意味着您的会话实体将不会更新为 不是数据库里的。为了解决这个问题,您可以 每个请求都将实体合并回实体管理器

虽然这解决了其中一个问题,但另一个常见问题是 (联合国)序列化。最终,您的用户实体将获得与的关系 其他对象,这会带来一些副作用:

如果延迟加载关系,则关系也将序列化 (标准设置),它将尝试序列化包含 联系。这将在屏幕上显示一些错误,因为 无法序列化连接。哦,别想了 更改实体(如添加字段)将导致 由于缺少,对象不完整的非序列化问题 财产。对于每个经过身份验证的用户,都会触发此情况

基本上,问题在于,如果您使用同一实体进行身份验证和处理用户/员工/客户等。您将遇到这样的问题:当您更改实体的属性时,将导致经过身份验证的用户与数据库中的内容不同步-导致角色不正确的问题,用户突然被迫注销(由于),或其他问题,具体取决于用户类在系统中的使用方式

我该如何解决这个问题? 假设:我假设您有一个“用户”实体,它至少有用户名、密码和角色

为了解决这个问题,我们需要创建两个单独的服务,作为用户实体和用户之间进行身份验证的桥梁

第一个是创建一个安全用户,它使用用户类中的字段

SecurityUser/app/Security/SecurityUser.php

<?php

namespace App\Security;

use App\Entity\User;
use Symfony\Component\Security\Core\User\UserInterface;

class SecurityUser implements UserInterface, \Serializable
{
    private $username;
    private $password;
    private $roles;

    public function __construct(User $user)
    {
        $this->username = $user->getUsername();
        $this->password = $user->getPassword();
        $this->roles = $user->getRoles();
    }

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

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

    public function getSalt()
    {
        // you *may* need a real salt depending on your encoder
        // see section on salt below
        return null;
    }

    /** @see \Serializable::serialize() */
    public function serialize()
    {
        return serialize(array(
            $this->username,
            $this->password,
            // Should only be set if your encoder uses a salt i.e. PBKDF2
            // This example uses Argon2i
            // $this->salt,
        ));
    }

    /** @see \Serializable::unserialize() */
    public function unserialize($serialized)
    {
        list (
            $this->username,
            $this->password,
            // Should only be set if your encoder uses a salt i.e. PBKDF2
            // This example uses Argon2i
            // $this->salt
            ) = unserialize($serialized, array('allowed_classes' => false));
    }

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

    public function eraseCredentials()
    {
    }
}
“db_provider”这个名字并不重要——你可以使用任何你想要的东西。此名称仅用于将提供程序映射到防火墙。如何配置防火墙稍微超出了本文档的范围,请参阅,以获取关于它的非常好的文档。不管怎样,如果出于某种原因,你对我的长相感到好奇(尽管我不会详细解释):

最后,我们需要配置一个编码器,以便能够加密密码

security:
    ...
    encoders:
        App\Security\SecurityUser:
            algorithm: argon2i
            memory_cost: 102400
            time_cost: 3
            threads: 4
旁注(主题外): 注意,我使用的是Argon2i。内存成本、时间成本和线程的值是非常主观的,具体取决于您的系统。您可以查看我的帖子,它可以帮助您获得系统的正确值

在这一点上,您的安全性应该可以正常工作,并且您已经完全与您的用户实体解耦了-恭喜

其他相关领域 现在您已经有了这个功能,也许您应该添加一些代码,这样您的用户会话在空闲这么长时间后就会被破坏。要做到这一点,请看我的答案

security:
    ...
    providers:
        db_provider:
            id: App\Security\SecurityUserProvider
security:
    ...
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            pattern:    ^/
            anonymous: ~
            provider: db_provider
            form_login:
                login_path: login
                check_path: login
            logout:
                path:   /logout
                target: /
                invalidate_session: true

    access_control:
        - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/, roles: ROLE_USER }
security:
    ...
    encoders:
        App\Security\SecurityUser:
            algorithm: argon2i
            memory_cost: 102400
            time_cost: 3
            threads: 4