身份验证问题$ user必须是UserInterface的实例

时间:2019-12-12 09:28:55

标签: php symfony symfony4

我正在使用Symfony 4.4,并且在用户身份验证方面有一个奇怪的问题。不幸的是,我本人无法重现该问题,但是我可以在日志中看到少数用户发生了该问题。出现以下错误信息:

  

$ user必须是UserInterface的实例,实现__toString方法的对象或原始字符串

Stacktrace:

{
    "class": "InvalidArgumentException",
    "message": "$user must be an instanceof UserInterface, an object implementing a __toString method, or a primitive string.",
    "code": 0,
    "file": "/var/www/vendor/symfony/security/Core/Authentication/Token/AbstractToken.php:95",
    "trace": [
        "/var/www/vendor/symfony/security/Http/Firewall/ContextListener.php:224",
        "/var/www/vendor/symfony/security/Http/Firewall/ContextListener.php:140",
        "/var/www/vendor/symfony/security/Http/Firewall/AbstractListener.php:27",
        "/var/www/vendor/symfony/security/Http/Firewall.php:139",
        "/var/www/vendor/symfony/security/Http/Firewall.php:129",
        "/var/www/vendor/symfony/security/Http/Firewall.php:97",
        "/var/www/vendor/symfony/event-dispatcher/EventDispatcher.php:304",
        "/var/www/vendor/symfony/event-dispatcher/EventDispatcher.php:264",
        "/var/www/vendor/symfony/event-dispatcher/EventDispatcher.php:239",
        "/var/www/vendor/symfony/event-dispatcher/EventDispatcher.php:73",
        "/var/www/vendor/symfony/http-kernel/HttpKernel.php:122",
        "/var/www/vendor/symfony/http-kernel/HttpKernel.php:68",
        "/var/www/vendor/symfony/http-kernel/Kernel.php:201",
        "/var/www/public/index.php:34"
    ]
}

我怀疑令牌可能无法更新,该消息主要出现在内部区域。有时,它也出现在首次访问该网站时,该用户可能尚未登录。

我的防火墙如下:

    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            pattern: ^/
            anonymous: ~
            form_login:
                login_path: login
                check_path: login_check
                username_parameter: login[username]
                password_parameter: login[password]
                csrf_token_generator: security.csrf.token_manager
            json_login:
                check_path: json_login_check
            provider: database
            logout:
                path: /logout
                target: /
            guard:
                provider: open_id
                authenticators:
                    - App\Path\To\OpenIdGuard

login_check 是默认的登录操作, json_login_check 是通过xhr的登录操作。另外,有两个用户提供程序,默认的一个(数据库)和另一个开放的ID( open_id )。

提供者如下:

    providers:
        database:
            entity:
                class: App\Entity\User
                property: email
        open_id:
            id: App\Path\To\Provider

用户实体(简体):

class User implements \Serializable, UserInterface, EncoderAwareInterface
{
    public function __toString(): string
    {
        return $this->getEmail();
    }

    public function __construct()
    {
        $this->salt = base_convert(sha1(uniqid(''.mt_rand(), true)), 16, 36);
        $this->setEnabled(false);
        $this->roles[] = 'ROLE_USER';
    }

    public function eraseCredentials(): void
    {
    }

    public function serialize(): string
    {
        return serialize(array(
            $this->id,
            $this->email,
            $this->password,
            $this->salt,
        ));
    }

    public function unserialize($serialized): void
    {
        list(
            $this->id,
            $this->email,
            $this->password,
            $this->salt
        ) = unserialize($serialized);
    }
}

两个提供程序(数据库和open_id)都使用相同的User实体。

出现问题是因为我很少说,在大多数情况下,一切都很好。

3 个答案:

答案 0 :(得分:1)

您的对象必须实现an object implementing a __toString method 因此您缺少其中的toString方法。 向User添加此方法

    /**
     * @return string
     */
    public function __toString()
    {
        return (string) $this->getUsername();
    }

应该做。 作为参考,请始终检查原始类: https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Model/User.php

编辑:由于您已经编辑了问题(通过向其添加toString)。我可以说,问题可能在于getEmail可能仍呈现一个非字符串的空值。请更改为

        return (string) $this->getEmail();

如果电子邮件为null,它将不属于“原始字符串”类别。 null是不实现UserInterface的对象。

答案 1 :(得分:1)

我现在能够找到并解决问题。更改用户组(ROLES)后,就会发生错误,因为无法刷新会话用户。

为了允许角色更改而不会注销用户或收到错误消息,必须在您的User实体中集成一个单独的isEqualTo()方法。参见:https://symfony.com/doc/current/security/user_provider.html#comparing-users-manually-with-equatableinterface

答案 2 :(得分:0)

我怀疑该问题出现在生产环境中的某个地方,因此,如果您有可能直接在服务器上更新代码,请按以下方式进行更新:

Symfony \ Component \ Security \ Core \ Authentication \ Token \ AbstractToken: (供应商/symfony/security-core/Authentication/Token/AbstractToken.php)

94.        if (!($user instanceof UserInterface || (\is_object($user) && method_exists($user, '__toString')) || \is_string($user))) {
95.            throw new \InvalidArgumentException('$user must be an instanceof UserInterface, an object implementing a __toString method, or a primitive string. ' . get_class($user) . ' given.');
96.        }

找出您在setUser()中收到的确切信息。

我怀疑您在null中有ContextListener

222.                $refreshedUser = $provider->refreshUser($user);
223.                $newToken = clone $token;
224.                $newToken->setUser($refreshedUser);

如果是这种情况,则应检查refreshUser()的{​​{1}}方法的情况。

实际上,这是我首先进行日志记录以检查正在发生什么的地方。