我从头开始设置了一个新的 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; } }
有人知道如何解决这个问题吗?
解决了!第一件事是让我的 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 或该检查是否有效,但是注释掉它让我能够登录: )