首頁  >  問答  >  主體

無法使用來自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 天前559

全部回覆(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
  • 取消回覆