我正在使用Symfony 4.4,并且在用户身份验证方面遇到了一个奇怪的问题。不幸的是,我自己无法重现该问题,但我可以在日志中看到它发生在我的少数用户身上。出现以下错误消息:
$user必须是 UserInterface 的实例、实现 __toString 方法的对象或基元字符串
堆栈跟踪:
{
"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:
- AppPathToOpenIdGuard
login_check是默认登录操作,并通过 xhrjson_login_check登录操作。此外,还有两个用户提供程序,默认一个(数据库(,另一个用于开放 id (open_id(。
提供程序如下所示:
providers:
database:
entity:
class: AppEntityUser
property: email
open_id:
id: AppPathToProvider
用户实体(简体(:
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(使用相同的用户实体。
正如我很少说的那样,出现问题,在大多数情况下,一切正常。
你的对象必须实现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
编辑:由于您已经编辑了问题(通过向其添加字符串(。我可以说它的问题可能是getEmail可能仍然呈现一个null,这不是一个字符串。请将其更改为
return (string) $this->getEmail();
如果电子邮件null
,则它不会属于"原始字符串"类别。 null 是不实现用户界面的对象。
我怀疑问题出现在生产环境中的某个地方,所以如果你有可能直接在服务器上更新代码,请像这样更新它:
Symfony\Component\Security\Core\Authentication\Token\AbstractToken:(vendor/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()
UserProvider
的方法发生了什么。
实际上,这是我放置一些日志记录以检查正在发生的事情的第一个地方。
我现在能够找到并解决问题。更改用户组 (ROLES( 后,由于无法刷新会话用户,因此会发生错误。
为了允许在不注销用户或收到错误消息的情况下更改角色,必须在用户实体中集成单独的 isEqualTo(( 方法。请参阅:https://symfony.com/doc/current/security/user_provider.html#comparing-users-manually-with-equatableinterface