我正在使用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实体。
出现问题是因为我很少说,在大多数情况下,一切都很好。
答案 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}}方法的情况。
实际上,这是我首先进行日志记录以检查正在发生什么的地方。