问题是,当我登录时在数据库中更改用户密码时,刷新页面后我的用户变得未经身份验证。
这会在我的浏览器中导致重定向过多错误,因为我的根控制器试图重定向到受@Security("is_granted('ROLE_ADMIN')")
注释保护的控制器。
SecurityController.php
/**
* @Route("/", name="app_login")
*/
public function login(AuthenticationUtils $authenticationUtils): Response
{
// (...)
return $this->redirectToRoute('admin');
}
AdminController.php
/**
* @Route("/admin", name="admin")
* @Security("is_granted('ROLE_ADMIN')", message="403 Access denied")
*/
public function index()
{
// (...)
}
刷新/admin
视图(或其他所有视图)后,由于注释@Security
而无法验证当前用户,并且在无限重定向循环开始时回退到根控制器。
如何防止Symfony在登录时更改密码后更改用户状态?
答案 0 :(得分:1)
getPassword()
方法。 PostAuthenticationGuardToken
会话密钥的序列化令牌'_security_main'
中存储经过身份验证(登录)的用户数据(也包括密码哈希),以及在容器内存储每个(普通或匿名)经过身份验证的用户令牌。要查看特定令牌内的数据,请执行以下操作:
dump(\unserialize($request->getSession()->get('_security_main')));
用于登录的用户dump($this->container->get('security.token_storage')->getToken());
PostAuthenticationGuardToken
用户令牌已更改为AnonymousToken
,因此is_granted('ROLE_ADMIN')
失败。检出名称空间Symfony\Component\Security\Core\Authentication\Token
:https://github.com/symfony/security-core/tree/4.2/Authentication/Token。
单击它,使下面的信息变得容易150%。
有AbstractToken
类是PostAuthenticationGuardToken
和AnonymousToken
的基础。 AbstractToken
具有hasUserChanged(UserInterface $user)
方法,该方法确定用户敏感数据是否已更改。
在该类内的是另一种方法setUser($user)
,该方法确定是否应该对用户进行身份验证(也基于hasUserChanged
),并为扩展该类的令牌类设置用户实体。>
AbstractToken.php
if ($changed) {
$this->setAuthenticated(false);
}
更改密码后未经身份验证的用户?!是的,但仅适用于抽象类。 普通用户和匿名用户均经过身份验证。很简单:
AnonymousToken.php
class AnonymousToken extends AbstractToken
{
private $secret;
public function __construct(string $secret, $user, array $roles = [])
{
parent::__construct($roles);
$this->secret = $secret;
$this->setUser($user);
$this->setAuthenticated(true); // here you go :-)
}
// (...)
现在出现所有问题的实际if
...
AbstractToken.php
if ($this->user->getPassword() !== $user->getPassword()) {
return true;
}
比较存储在会话中的用户和提供者的refreshUser返回的用户。 Symfony & Guard: "The security token was removed due to an AccountStatusException"
功劳归于:Alain Tiemblo
在您的用户实体中实施EquatableInterface
User.php
use Symfony\Component\Security\Core\User\{UserInterface, EquatableInterface};
class User implements UserInterface, EquatableInterface
{
// (...)
}
从此界面覆盖isEqualTo
方法
User.php
public function isEqualTo(UserInterface $user)
{
return $user->getId() === $this->getId();
}
完成。现在,每次在数据库中更改密码后调用hasUserChanged
时,都不会执行密码检查if
。
证明:
AbstractToken.php
private function hasUserChanged(UserInterface $user)
{
if (!($this->user instanceof UserInterface)) {
throw new \BadMethodCallException('Method "hasUserChanged" should be called when current user class is instance of "UserInterface".');
}
if ($this->user instanceof EquatableInterface) {
return !(bool) $this->user->isEqualTo($user);
// THIS is executed and FALSE is returned because user instances have the same ids but different passwords :)
}
if ($this->user->getPassword() !== $user->getPassword()) {
return true;
}
// other checks like salt or username below
现在,您可以回到2.1和2.2并检查结果。