我已经从头开始设置了一个新的6.1站点,并正在尝试实现Security。我有一个来自Symfony 3.4以前网站的用户数据库,我正在尝试使用它,其中包含现有的密码哈希和Salts-我希望继续使用相同的哈希,因此使用了sha1算法(稍后将考虑升级哈希算法(。尝试登录总是返回以下信息:
#message: "The presented password is invalid."
#code: 0
#file: "/** redacted **/vendor/symfony/security-http/EventListener/CheckCredentialsListener.php"
#line: 69
#serialized: null
-token: null
trace: {▼
/** redacted **/vendor/symfony/security-http/EventListener/CheckCredentialsListener.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版本网站使用的数据库中的密码相同,使用的密码哈希器完全相同。
这基本上是一个开箱即用的网站,除了尝试让安全性发挥作用之外,我没有做任何配置,我完全遵循了文档。
这是我的安全
# 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,
]
);
}
}
这是我的登录名。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, '$')) {
开头的块。
由于$this->hashLength
始终为-1,因此此块永久返回false。$this->hashLength
是在构造函数中设置的,我不知道为什么它总是返回-1,也不知道该检查是否有效,但注释它使我能够登录:(