- Вопрос или проблема
- Ответ или решение
- Разбор проблемы: API Platform 4 + Symfony 7 + JWT аутентификация — неверные учетные данные
- 1. Проверка настройки безопасности в security.yaml
- 2. Проверка формирования и валидации JWT
- 3. Пользовательская сущность и протокол аутентификации
- 4. Проверка данных из файла .env
- Заключение
Вопрос или проблема
Попробуйте создать проект API с использованием последнего стека SF7, APIPlatform 4 и JWT.
API работает хорошо для генерации JWT токена в URL \auth
НО всегда возвращает мне ОШИБКУ: 401 Неверные учетные данные
Много попыток решить эту проблему в интернете и на stackOverflow, но ничего не помогает.
Возможно, проблема или ошибка связана с конкретным стеком…
Буду благодарен за ваши советы
Эти требования выполнены:
-
генерация JWT ключа
-
активация JWT lexik bundle в bundles.php
-
getUserIdentifier() установлен в User Entity
security.yaml
security:
password_hashers:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
providers:
app_user_provider:
entity:
class: App\Entity\Users
property: email
firewalls:
dev:
pattern: ^/_(profiler|wdt)
security: false
main:
stateless: true
provider: app_user_provider
json_login:
check_path: auth
username_path: email
password_path: password
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
jwt: ~
access_control:
- { path: ^/$, roles: PUBLIC_ACCESS }
- { path: ^/docs, roles: PUBLIC_ACCESS }
- { path: ^/auth, roles: PUBLIC_ACCESS }
- { path: ^/, roles: IS_AUTHENTICATED_FULLY }
lexik_jwt_authentication.yaml
lexik_jwt_authentication:
secret_key: '%env(resolve:JWT_SECRET_KEY)%'
public_key: '%env(resolve:JWT_PUBLIC_KEY)%'
pass_phrase: '%env(resolve:JWT_PASSPHRASE)%'
routes/api_platform.yaml
api_platform:
resource: .
type: api_platform
prefix: /api
routes.yaml
auth:
path: /auth
methods: ['POST']
controllers:
resource:
path: ../src/Controller/
namespace: App\Controller
type: attribute
UserFixtures.php :
$user1 = new Users();
$user1->setCivility('mr');
$user1->setEmail('[email protected]');
$user1->setRoles(['ROLE_SUPER_ADMIN']);
$hashedPassword = $this->hasher->hashPassword($user1, 'rere-toto');
$user1->setPassword($hashedPassword);
$user1->setUsername('jordy');
$user1->setFirstName('michael');
$user1->setLastName('jordan');
$manager->persist($user1);
$manager->flush();
Users.php (Сущность)
<?php
namespace App\Entity;
use ApiPlatform\Metadata\ApiResource;
use App\Repository\UsersRepository;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\Mapping\JoinColumn;
use Doctrine\ORM\Mapping\ManyToOne;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
#[ORM\Entity(repositoryClass: UsersRepository::class)]
#[ORM\UniqueConstraint(name: 'UNIQ_IDENTIFIER_EMAIL', fields: ['email'])]
#[UniqueEntity(fields: ['email'], message: 'Учетная запись с этим email уже существует')]
#[ApiResource]
class Users implements UserInterface, PasswordAuthenticatedUserInterface
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 180)]
private ?string $email = null;
#[ORM\Column]
private array $roles = [];
#[ORM\Column(type: 'string')]
private ?string $password = null;
#[ORM\Column(type: 'boolean')]
private bool $isVerified = false;
#[ORM\Column(type: 'string')]
private string $civility;
#[ORM\Column(type: 'string')]
private string $firstName;
#[ORM\Column(type: 'string')]
private string $lastName;
#[ORM\Column(type: 'string')]
private string $username;
#[ORM\Column(type: 'blob', nullable: true)]
private string $picture;
#[ORM\Column(type: 'string', length: 2)]
private string $locale="fr";
public function getId(): ?int
{
return $this->id;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): static
{
$this->email = $email;
return $this;
}
public function getUserIdentifier(): string
{
return (string) $this->email;
}
public function getRoles(): array
{
$roles = $this->roles;
// гарантирует, что каждый пользователь имеет хотя бы ROLE_USER
$roles[] = 'ROLE_USER';
return array_unique($roles);
}
public function setRoles(array $roles): static
{
$this->roles = $roles;
return $this;
}
public function getPassword(): string
{
return $this->password;
}
public function setPassword(string $password): static
{
$this->password = $password;
return $this;
}
public function getCivility(): string
{
return $this->civility;
}
public function setCivility(string $civility): void
{
$this->civility = $civility;
}
public function getFirstName(): string
{
return $this->firstName;
}
public function setFirstName(string $firstName): void
{
$this->firstName = $firstName;
}
public function getLastName(): string
{
return $this->lastName;
}
public function setLastName(string $lastName): void
{
$this->lastName = $lastName;
}
public function getUsername(): string
{
return $this->username;
// return $this->email;
}
public function setUsername(string $username): void
{
$this->username = $username;
}
public function getLocale(): string
{
return $this->locale;
}
public function setLocale(string $locale): void
{
$this->locale = $locale;
}
public function eraseCredentials(): void
{
// Если у вас есть какие-либо временные, чувствительные данные о пользователе, очистите их здесь
// $this->plainPassword = null;
}
public function getIsVerified(): bool
{
return $this->isVerified;
}
public function setIsVerified(bool $isVerified): static
{
$this->isVerified = $isVerified;
return $this;
}
public function getPicture(): string
{
return $this->picture;
}
public function setPicture(string $picture): void
{
$this->picture = $picture;
}
}
.env
JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
JWT_PASSPHRASE=a0b919dGJxxxxxxcaa29fkh56bxxxxx2fde41f13381b7dac864
Ответ или решение
Разбор проблемы: API Platform 4 + Symfony 7 + JWT аутентификация — неверные учетные данные
Ваша проблема с получением ошибки 401 Invalid credentials
при попытке аутентификации через JWT в стеке Symfony 7 и API Platform 4 может быть вызвана несколькими факторами. Давайте рассмотрим решение шаг за шагом.
1. Проверка настройки безопасности в security.yaml
Ваш файл конфигурации security.yaml
выглядит в целом корректно. Однако важно убедиться, что все настроено как следует:
security:
password_hashers:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
providers:
app_user_provider:
entity:
class: App\Entity\Users
property: email
firewalls:
dev:
pattern: ^/_(profiler|wdt)
security: false
main:
stateless: true
provider: app_user_provider
json_login:
check_path: auth
username_path: email
password_path: password
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
jwt: ~
access_control:
- { path: ^/$, roles: PUBLIC_ACCESS }
- { path: ^/docs, roles: PUBLIC_ACCESS }
- { path: ^/auth, roles: PUBLIC_ACCESS }
- { path: ^/, roles: IS_AUTHENTICATED_FULLY }
Проверьте следующие моменты:
- Убедитесь, что путь
auth
правильно настроен и доступен для POST-запросов. Также проверьте, что настройки CORS не блокируют ваш запрос, если вы используете его из браузера. - Убедитесь, что вы передаете правильные данные (email и password) в запросе.
2. Проверка формирования и валидации JWT
Ваша настройка для lexik_jwt_authentication.yaml
:
lexik_jwt_authentication:
secret_key: '%env(resolve:JWT_SECRET_KEY)%'
public_key: '%env(resolve:JWT_PUBLIC_KEY)%'
pass_phrase: '%env(resolve:JWT_PASSPHRASE)%'
Убедитесь, что:
- Путь к файлам ключей (
private.pem
иpublic.pem
) актуален и файлы существуют. - Пароль (passphrase) для ключа приватного API корректный.
Для генерации токена попробуйте с помощью команды Symfony:
php bin/console lexik:jwt:generate-token <user_id>
Это позволит удостовериться, что ключи и пароли настроены правильно.
3. Пользовательская сущность и протокол аутентификации
Ваш класс Users
реализует интерфейсы UserInterface
и PasswordAuthenticatedUserInterface
, что является правильным. Однако, стоит обратить внимание на метод getUserIdentifier()
. Он должен возвращать уникальный идентификатор пользователя, который вы используете для аутентификации. Убедитесь, что метод возвращает корректное значение.
public function getUserIdentifier(): string
{
return (string) $this->email; // Убедитесь, что email действительно уникален
}
Также убедитесь, что пользователь с указанным email
существует и его пароль захеширован корректно.
4. Проверка данных из файла .env
Ваш env
файл должен содержать корректные значения ключей и паролей. Неправильная конфигурация может вести к ошибкам аутентификации. Например:
JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
JWT_PASSPHRASE=ваш_пароль
Заключение
Если после выполнения всех вышеуказанных рекомендаций проблема сохраняется, попробуйте отладить процесс аутентификации, добавив временные сообщения в обработчики успешной и неуспешной аутентификации, чтобы лучше понять, где происходит сбой.
Также проверьте логи Symfony и консольные команды для обнаружения возможных ошибок. Успехов в решении проблемы!