【问题标题】:How to prevent user from being unauthenticated after dynamic password change如何防止用户在动态密码更改后未经身份验证
【发布时间】:2020-04-02 10:54:33
【问题描述】:

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

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

【问题讨论】:

    标签: php symfony4


    【解决方案1】:

    前奏

    1. Symfony 对每个请求的用户进行身份验证,因此每次刷新视图时它都会从用户实体调用getPassword() 方法。
    2. Symfony 仅在'_security_main' 会话密钥中的序列化令牌PostAuthenticationGuardToken 中存储经过身份验证(登录)的用户数据(也是密码哈希),以及容器内的每个(普通或匿名)经过身份验证的用户令牌。要查看特定令牌中的数据,请使用:
      1. dump(\unserialize($request->getSession()->get('_security_main'))); 登录用户
      2. 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 的基础。 AbstractTokenhasUserChanged(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

    用户.php

    use Symfony\Component\Security\Core\User\{UserInterface, EquatableInterface};
    
    class User implements UserInterface, EquatableInterface
    {
    // (...)
    }
    

    从此接口覆盖isEqualTo方法

    用户.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 并检查结果。

    【讨论】:

      猜你喜欢
      • 2011-09-10
      • 2021-07-08
      • 2013-05-31
      • 2017-01-27
      • 1970-01-01
      • 2016-07-02
      • 1970-01-01
      • 2014-04-27
      • 2020-03-26
      相关资源
      最近更新 更多