首页  >  问答  >  正文

无法使用来自3.4网站的用户数据库登录新网站的Symfony 6.1问题

我从头开始设置了一个新的 6.1 站点,并正在尝试实施安全性。我有一个来自 Symfony 3.4 中以前站点的用户数据库,我正在尝试使用它,其中包含现有的密码哈希值和盐 - 我希望继续使用相同的哈希值,因此使用 sha1 算法(稍后将考虑升级哈希算法)。尝试登录总是返回以下内容:

#message: "The presented password is invalid."
  #code: 0
  #file: "/** redacted **/vendor/symfony/security-http/EventLis tener/CheckCredentialsLis tener.php"
  #line: 69
  #serialized: null
  -token: null
  trace: {▼
    /** redacted **/vendor/symfony/security-http/EventLis tener/CheckCredentialsLis tener.php:69 {▶}
    /** redacted **/vendor/symfony/http-foundation/Session/Storage/NativeSessionStorage.php:175 {▶}
    /** redacted **/vendor/symfony/http-foundation/Session/Storage/NativeSessionStorage.php:326 {▶}
    /** redacted **/vendor/symfony/http-foundation/Session/Session.php:258 {▶}
    /** redacted **/vendor/symfony/http-foundation/Session/Session.php:278 {▶}
    /** redacted **/vendor/symfony/http-foundation/Session/Session.php:70 {▶}
    /** redacted **/vendor/symfony/security-http/Authentication/AuthenticationUtils.php:40 {▶}
    /** redacted **/src/Controller/SecurityController.php:33 {▶}
    /** redacted **/vendor/symfony/http-kernel/HttpKernel.php:153 {▶}
    /** redacted **/vendor/symfony/http-kernel/HttpKernel.php:75 {▶}
    /** redacted **/vendor/symfony/http-kernel/Kernel.php:202 {▶}
    /** redacted **/vendor/symfony/runtime/Runner/Symfony/HttpKernelRunner.php:35 {▶}
    /** redacted **/vendor/autoload_runtime.php:29 {▶}
    /** redacted **/public/index.php:5 {▶}
  }
}

这看起来很简单,它无法识别密码。但是,该密码与 Symfony 3.4 版本站点使用的数据库中的密码相同,并且使用完全相同的密码哈希器。

这基本上是一个开箱即用的网站,除了尝试让安全性正常工作之外,我没有进行任何配置,我完全遵循了文档。

这是我的 security.yaml:

# https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
    password_hashers:
        AppEntityUsers:
            algorithm:   sha1
            iterations: 1
            encode_as_base64: false

    # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
    providers:
        app_user_provider:
            entity:
                class: AppEntityUsers
                property: email

    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            lazy: true
            provider: app_user_provider
            form_login:
                login_path: login
                check_path: login
                enable_csrf: false
            login_throttling:
                max_attempts: 3 # per minute
                interval: '15 minutes'

这里是SecurityController.php(包含路由/登录):

namespace AppController;

use AppEntityOffice;
use DoctrinePersistenceManagerRegistry;
use SymfonyBundleFrameworkBundleControllerAbstractController;
use SymfonyComponentHttpFoundationRequest;
use SymfonyComponentHttpFoundationResponse;
use SymfonyComponentHttpFoundationSessionSession;
use SymfonyComponentHttpFoundationSessionStorageHandlerNativeFileSessionHandler;
use SymfonyComponentHttpFoundationSessionStorageNativeSessionStorage;
use SymfonyComponentRoutingAnnotationRoute;
use SymfonyComponentSecurityCoreSecurity;
use SymfonyComponentSecurityHttpAuthenticationAuthenticationUtils;
use PsrLogLoggerInterface;

class SecurityController extends AbstractController
{
    public function __construct(private ManagerRegistry $doctrine, private LoggerInterface $logger) {}

    #[Route('/login', name: 'login')]
    public function login(Request $request, AuthenticationUtils $authenticationUtils): Response
    {
        $sessionStorage = new NativeSessionStorage([], new NativeFileSessionHandler());
        $session = new Session($sessionStorage);

        $doctrine    =  $this->doctrine;
        $logger  =  $this->logger;
        $logger->info('loginAction');

        // get the login error if there is one
        $error = $authenticationUtils->getLastAuthenticationError();

        // last username entered by the user
        $lastUsername = $authenticationUtils->getLastUsername();

        return $this->render(
            'security/login.html.twig',
            [
                'controller_name' => 'SecurityController',
                // last username entered by the user
                'last_username' => $lastUsername,
                'error'         => $error,
            ]
        );
    }
}

这是我的login.html.twig:

{% block PageHeader %}{% endblock %}

{% block PageContent %}
    <div style="min-height: 250px;">
        <h1>{{ 'security.login.title'|trans({}) }}</h1>
        <br />

        {% if error %}
            {{ dump(error) }}
            {% if error.messageKey is defined %}
                <div class="error">{{ error.messageKey|trans(error.messageData) }}</div>
            {% endif %}
        {% endif %}

        <form action="{{ path('login') }}" method="post" class="login">
            <label for="username">{{ 'security.login.username'|trans({}) }}</label>
            <input type="text" id="username" name="_username" value="{{ last_username }}" required="required" />

            <br /><br />
            <label for="password">{{ 'security.login.password'|trans({}) }}</label>
            <input type="password" id="password" name="_password" required="required" />
            
            <br />
            <input type="hidden" name="_csrf_token" value="{{ csrf_token('authenticate') }}">
            <input type="submit" id="_submit" name="_submit" value="{{ 'security.login.submit'|trans({}) }}" />
        </form>
    </div>
{% endblock %}

{% block PageFooter %}{% endblock %}

这是我的用户类:

namespace AppEntity;

use DoctrineCommonCollectionsArrayCollection;
use DoctrineCommonCollectionsCollection;
use DoctrineDBALTypesTypes;
use DoctrineORMMapping as ORM;

use AppRepositoryUsersRepository;
use SymfonyComponentSecurityCoreUserPasswordAuthenticatedUserInterface;
use SymfonyComponentSecurityCoreUserUserInterface;

/**
 * Users
 *
 * @ORMTable(name="Users", indexes={@ORMIndex(name="IDX_D5428AED73FD6E34", columns={"Office"})})
 * @ORMEntity(repositoryClass="AppRepositoryUsersRepository")
 */
class Users implements UserInterface, PasswordAuthenticatedUserInterface
{
    /**
     * @var int
     *
     * @ORMColumn(name="UserId", type="integer", nullable=false)
     * @ORMId
     * @ORMGeneratedValue(strategy="IDENTITY")
     */
    private $userid;

    /**
     * @var string|null
     *
     * @ORMColumn(name="Firstname", type="string", length=100, nullable=true)
     */
    private $firstname;

    /**
     * @var string|null
     *
     * @ORMColumn(name="Surname", type="string", length=100, nullable=true)
     */
    private $surname;

    /**
     * @var string
     *
     * @ORMColumn(name="Email", type="string", length=150, nullable=false)
     */
    private $email;

    /**
     * @var string|null
     *
     * @ORMColumn(name="JobTitle", type="string", length=150, nullable=true)
     */
    private $jobtitle;

    /**
     * @var string|null
     *
     * @ORMColumn(name="Password", type="string", length=150, nullable=true)
     */
    private $password;

    /**
     * @var string|null
     *
     * @ORMColumn(name="Salt", type="string", length=50, nullable=true)
     */
    private $salt;

    /**
     * @var int|null
     *
     * @ORMColumn(name="Status", type="integer", nullable=true)
     */
    private $status;

    /**
     * @var DateTime|null
     *
     * @ORMColumn(name="CreatedOn", type="datetime", nullable=true)
     */
    private $createdon;

    /**
     * @var int|null
     *
     * @ORMColumn(name="CreatedBy", type="integer", nullable=true)
     */
    private $createdby;

    /**
     * @var DateTime|null
     *
     * @ORMColumn(name="LastUpdatedOn", type="datetime", nullable=true)
     */
    private $lastupdatedon;

    /**
     * @var int|null
     *
     * @ORMColumn(name="LastUpdatedBy", type="integer", nullable=true)
     */
    private $lastupdatedby;

    /**
     * @var DateTime|null
     *
     * @ORMColumn(name="Deleted", type="datetime", nullable=true)
     */
    private $deleted;

    /**
     * @var Office
     *
     * @ORMManyToOne(targetEntity="Office")
     * @ORMJoinColumns({
     *   @ORMJoinColumn(name="Office", referencedColumnName="OfficeId")
     * })
     */
    private $office;

    /**
     * @var DoctrineCommonCollectionsCollection
     *
     * @ORMManyToMany(targetEntity="Usergroup", inversedBy="userid")
     * @ORMJoinTable(name="usergroupmap",
     *   joinColumns={
     *     @ORMJoinColumn(name="UserId", referencedColumnName="UserId")
     *   },
     *   inverseJoinColumns={
     *     @ORMJoinColumn(name="UserGroup", referencedColumnName="UserGroupId")
     *   }
     * )
     */
    private $usergroup = array();

    /**
     * Constructor
     */
    public function __construct()
    {
        $this->usergroup = new DoctrineCommonCollectionsArrayCollection();
    }

    public function getUserid(): ?int
    {
        return $this->userid;
    }

    public function getFirstname(): ?string
    {
        return $this->firstname;
    }

    public function setFirstname(?string $firstname): self
    {
        $this->firstname = $firstname;

        return $this;
    }

    public function getSurname(): ?string
    {
        return $this->surname;
    }

    public function setSurname(?string $surname): self
    {
        $this->surname = $surname;

        return $this;
    }

    public function getEmail(): ?string
    {
        return $this->email;
    }

    public function setEmail(string $email): self
    {
        $this->email = $email;

        return $this;
    }

    public function getJobtitle(): ?string
    {
        return $this->jobtitle;
    }

    public function setJobtitle(?string $jobtitle): self
    {
        $this->jobtitle = $jobtitle;

        return $this;
    }

    public function getPassword(): ?string
    {
        return $this->password;
    }

    public function setPassword(?string $password): self
    {
        $this->password = $password;

        return $this;
    }

    public function getSalt(): ?string
    {
        return $this->salt;
    }

    public function setSalt(?string $salt): self
    {
        $this->salt = $salt;

        return $this;
    }

    public function getStatus(): ?int
    {
        return $this->status;
    }

    public function setStatus(?int $status): self
    {
        $this->status = $status;

        return $this;
    }

    public function getCreatedon(): ?DateTimeInterface
    {
        return $this->createdon;
    }

    public function setCreatedon(?DateTimeInterface $createdon): self
    {
        $this->createdon = $createdon;

        return $this;
    }

    public function getCreatedby(): ?int
    {
        return $this->createdby;
    }

    public function setCreatedby(?int $createdby): self
    {
        $this->createdby = $createdby;

        return $this;
    }

    public function getLastupdatedon(): ?DateTimeInterface
    {
        return $this->lastupdatedon;
    }

    public function setLastupdatedon(?DateTimeInterface $lastupdatedon): self
    {
        $this->lastupdatedon = $lastupdatedon;

        return $this;
    }

    public function getLastupdatedby(): ?int
    {
        return $this->lastupdatedby;
    }

    public function setLastupdatedby(?int $lastupdatedby): self
    {
        $this->lastupdatedby = $lastupdatedby;

        return $this;
    }

    public function getDeleted(): ?DateTimeInterface
    {
        return $this->deleted;
    }

    public function setDeleted(?DateTimeInterface $deleted): self
    {
        $this->deleted = $deleted;

        return $this;
    }

    public function getOffice(): ?Office
    {
        return $this->office;
    }

    public function setOffice(?Office $office): self
    {
        $this->office = $office;

        return $this;
    }

    /**
     * @return Collection<int, Usergroup>
     */
    public function getUsergroup(): Collection
    {
        return $this->usergroup;
    }

    public function addUsergroup(Usergroup $usergroup): self
    {
        if (!$this->usergroup->contains($usergroup)) {
            $this->usergroup->add($usergroup);
        }

        return $this;
    }

    public function removeUsergroup(Usergroup $usergroup): self
    {
        $this->usergroup->removeElement($usergroup);

        return $this;
    }

    /**
     * @see UserInterface
     */
    public function eraseCredentials(): array
    {
        // If you store any temporary, sensitive data on the user, clear it here
    }

    /**
     * @see UserInterface
     */
    public function getRoles(): array
    {
        foreach ($this->usergroup as $key => $value)
        {
            $roles[] = $value->getUsergroup();
        }
        return array_unique($roles);
    }

    /**
     * A visual identifier that represents this user.
     *
     * @see UserInterface
     */
    public function getUserIdentifier(): string
    {
        return (string) $this->email;
    }
}

有人知道如何解决这个问题吗?

P粉129168206P粉129168206259 天前560

全部回复(1)我来回复

  • P粉523335026

    P粉5233350262024-01-09 11:44:03

    解决了!第一件事是让我的 User 类继承 LegacyPasswordAuthenticatedUserInterface 而不是 PasswordAuthenticatedUserInterface

    第二件事是查看 /vendor/symfony/password-hasher/Hasher/MessageDigestPasswordHasher.php,并在 verify 函数中,注释掉开头的块行 if (\strlen($hashedPassword) !== $this->hashLength || str_contains($hashedPassword, '$')) {.

    此块永久返回 false,因为 $this->hashLength 始终为 -1。 $this->hashLength 在构造函数中设置,我不知道为什么它总是返回 -1 或该检查是否有效,但是注释掉它让我能够登录: )

    回复
    0
  • 取消回复