动态密码更改后如何防止用户未经身份验证

时间:2019-12-09 10:43:38

标签: php symfony4

问题是,当我登录时在数据库中更改用户密码时,刷新页面后我的用户变得未经身份验证

这会在我的浏览器中导致重定向过多错误,因为我的根控制器试图重定向到受@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在登录时更改密码后更改用户状态?

1 个答案:

答案 0 :(得分:1)

序曲

  1. Symfony在每个请求上对用户进行身份验证,因此,每次刷新视图时,Symfony都会从用户实体调用getPassword()方法。
  2. Symfony仅在PostAuthenticationGuardToken会话密钥的序列化令牌'_security_main'中存储经过身份验证(登录)的用户数据(也包括密码哈希),以及在容器内存储每个(普通或匿名)经过身份验证的用户令牌。要查看特定令牌内的数据,请执行以下操作:
    1. dump(\unserialize($request->getSession()->get('_security_main')));用于登录的用户
    2. 为每个用户
    3. dump($this->container->get('security.token_storage')->getToken());
  3. 在数据库中更改密码并同时登录时,PostAuthenticationGuardToken用户令牌已更改为AnonymousToken,因此is_granted('ROLE_ADMIN')失败。

幕后

检出名称空间Symfony\Component\Security\Core\Authentication\Tokenhttps://github.com/symfony/security-core/tree/4.2/Authentication/Token单击它,使下面的信息变得容易150%。

AbstractToken类是PostAuthenticationGuardTokenAnonymousToken的基础。 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并检查结果。