Я пытаюсь интегрировать аутентификацию JWT в мой проект платформы API.Я могу зарегистрировать пользователя, но не могу получить токен JWT от этого созданного пользователя.
При отправке запроса с помощью curl: curl -X POST -H "Content-Type: application/json" http://localhost:8000/login_check -d '{"username":"johndoe","password":"test"}'
Я получаю ошибку: Uncaught PHP Exception Symfony\Component\HttpKernel\Exception\BadRequestHttpException: "Invalid JSON." at (...)\vendor\symfony\security-http\Firewall\UsernamePasswordJsonAuthenticationListener.php line 92
Вот мой пользовательский объект:
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiFilter;
use ApiPlatform\Core\Annotation\ApiProperty;
use ApiPlatform\Core\Annotation\ApiSubresource;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
use ApiPlatform\Core\Annotation\ApiResource;
use Symfony\Component\Serializer\Annotation\Groups;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\SearchFilter;
/**
* @ORM\Entity(repositoryClass="App\Repository\UserRepository")
* @ApiResource(normalizationContext={"groups"={"user"}})
* @ApiFilter(SearchFilter::class, properties={"centres.id": "exact"})
*/
class User implements UserInterface
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
* @Groups({"user"})
*/
private $id;
/**
* @ORM\Column(type="string", length=50)
* @Groups({"user"})
*/
private $username;
/**
* @ORM\Column(type="string", length=64)
* @Groups({"user"})
*/
private $password;
/**
* @ORM\Column(type="string", length=50, nullable=true)
* @Groups({"user"})
*/
private $prenom;
/**
* @ORM\Column(type="string", length=50, nullable=true)
* @Groups({"user"})
*/
private $nom;
/**
* @ORM\Column(type="string", length=80)
* @Groups({"user"})
*/
private $email;
/**
* @ORM\Column(type="array")
* @Groups({"user"})
*/
private $roles = [];
/**
* @ORM\Column(type="datetime", nullable=true)
* @Groups({"user"})
*/
private $dateNaissance;
/**
* @ORM\Column(type="datetime")
* @Groups({"user"})
*/
private $dateEnregistrement;
/**
* @ORM\Column(type="datetime", nullable=true)
* @Groups({"user"})
*/
private $dateDernierePartie;
/**
* @ORM\Column(type="boolean")
* @Groups({"user"})
*/
private $actif;
/**
* @ORM\Column(type="integer")
* @Groups({"user"})
*/
private $niveau;
/**
* @ORM\Column(type="integer")
* @Groups({"user"})
*/
private $experience;
/**
* @ORM\Column(type="integer")
* @Groups({"user"})
*/
private $nbVictimes;
/**
* @ORM\Column(type="integer")
* @Groups({"user"})
*/
private $nbMorts;
/**
* @ORM\Column(type="integer", nullable=true)
* @Groups({"user"})
*/
private $justesse;
/**
* @ORM\Column(type="integer", nullable=true)
* @Groups({"user"})
*/
private $nbParties;
/**
* @ORM\OneToMany(targetEntity="App\Entity\Carte", mappedBy="client")
* @Groups({"user"})
* @var Collection
*/
private $cartes;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Equipe", inversedBy="joueurs")
* @ORM\JoinColumn(nullable=true)
* @Groups({"user"})
*/
private $equipe;
/**
* @ORM\ManyToMany(targetEntity="App\Entity\Centre", inversedBy="clients")
* @ORM\JoinTable(name="users_centres")
* @var Collection
* @Groups({"user"})
*/
private $centres;
public function __construct()
{
$this->cartes = new ArrayCollection();
$this->centres = new ArrayCollection();
$this->actif = true;
$this->niveau = 1;
$this->experience = 0;
$this->nbVictimes = 0;
$this->nbMorts = 0;
$this->justesse = 0;
$this->nbParties = 0;
$this->dateEnregistrement = new \DateTime();
}
/**
* @param int|null $id
* @param string $surnom
* @param string $email
* @param string $password
* @param array $roles
* @param \DateTime|null $dateEnregistrement
* @return User
*/
static public function creer(
?int $id = null,
string $surnom,
string $email,
string $password,
array $roles,
?\DateTime $dateEnregistrement = null
)
{
$user = new self();
$user->id = $id;
$user->username = $surnom;
$user->email = $email;
$user->password = $password;
$user->roles = $roles;
$user->dateEnregistrement = $dateEnregistrement;
return $user;
}
public function addCarte(Carte $carte)
{
if ($this->cartes->contains($carte)) {
return;
}
$this->cartes->add($carte);
$carte->setClient($this);
}
public function addCentre(Centre $centre)
{
if ($this->centres->contains($centre)) {
return;
}
$this->centres->add($centre);
//$centre->inscrireJoueur($this);
}
public function ajouterNbVictimes(int $nbVictimes)
{
$this->nbVictimes += $nbVictimes;
}
public function ajouterJustesse(int $justesse)
{
$this->justesse += $justesse;
}
public function diminuerJustesse(int $justesse)
{
$this->justesse -= $justesse;
}
public function ajouterNbMorts(int $nbMorts)
{
$this->nbMorts += $nbMorts;
}
public function getId(): ?int
{
return $this->id;
}
public function setUsername(string $username): self
{
$this->username = $username;
return $this;
}
public function getPassword(): ?string
{
return $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
public function getPrenom(): ?string
{
return $this->prenom;
}
public function setPrenom(string $prenom): self
{
$this->prenom = $prenom;
return $this;
}
public function getNom(): ?string
{
return $this->nom;
}
public function setNom(string $nom): self
{
$this->nom = $nom;
return $this;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
public function getRoles(): ?array
{
return $this->roles;
}
public function setRoles(array $roles): self
{
$this->roles = $roles;
return $this;
}
public function getDateNaissance(): ?\DateTimeInterface
{
return $this->dateNaissance;
}
public function setDateNaissance(\DateTimeInterface $dateNaissance): self
{
$this->dateNaissance = $dateNaissance;
return $this;
}
public function getDateEnregistrement(): ?\DateTimeInterface
{
return $this->dateEnregistrement;
}
public function setDateEnregistrement(\DateTimeInterface $dateEnregistrement): self
{
$this->dateEnregistrement = $dateEnregistrement;
return $this;
}
public function getDateDernierePartie(): ?\DateTimeInterface
{
return $this->dateDernierePartie;
}
public function setDateDernierePartie(?\DateTimeInterface $dateDernierePartie): self
{
$this->dateDernierePartie = $dateDernierePartie;
return $this;
}
public function getActif(): ?bool
{
return $this->actif;
}
public function setActif(bool $actif): self
{
$this->actif = $actif;
return $this;
}
public function getNiveau(): ?int
{
return $this->niveau;
}
public function setNiveau(int $niveau): self
{
$this->niveau = $niveau;
return $this;
}
public function getExperience(): ?int
{
return $this->experience;
}
public function setExperience(int $experience): self
{
$this->experience = $experience;
return $this;
}
public function getNbVictimes(): ?int
{
return $this->nbVictimes;
}
public function setNbVictimes(int $nbVictimes): self
{
$this->nbVictimes = $nbVictimes;
return $this;
}
public function getNbMorts(): ?int
{
return $this->nbMorts;
}
public function setNbMorts(int $nbMorts): self
{
$this->nbMorts = $nbMorts;
return $this;
}
public function getJustesse(): ?int
{
return $this->justesse;
}
public function setJustesse(int $justesse): self
{
$this->justesse = $justesse;
return $this;
}
/**
* @return mixed
*/
public function getNbParties()
{
return $this->nbParties;
}
/**
* @param mixed $nbParties
*/
public function setNbParties($nbParties): void
{
$this->nbParties = $nbParties;
}
/**
* @return mixed
*/
public function getCartes()
{
return $this->cartes;
}
/**
* @param mixed $cartes
*/
public function setCartes($cartes): void
{
$this->cartes = $cartes;
}
/**
* @return mixed
*/
public function getEquipe()
{
return $this->equipe;
}
/**
* @param mixed $equipe
*/
public function setEquipe($equipe): void
{
$this->equipe = $equipe;
}
/**
* @return mixed
*/
public function getCentres()
{
return $this->centres;
}
/**
* @param mixed $centre
*/
public function setCentres($centres): void
{
$this->centres = $centres;
}
/**
* Returns the salt that was originally used to encode the password.
*
* This can return null if the password was not encoded using a salt.
*
* @return string|null The salt
*/
public function getSalt()
{
return null;
}
/**
* Returns the username used to authenticate the user.
*
* @return string The username
*/
public function getUsername()
{
return $this->username;
}
/**
* Removes sensitive data from the user.
*
* This is important if, at any given point, sensitive information like
* the plain-text password is stored on this object.
*/
public function eraseCredentials()
{
}
}
Мои маршруты.yaml:
register:
path: /register
controller: App\Controller\AuthController::register
methods: POST
api:
path: /api
controller: App\Controller\AuthController::api
login_check:
path: /login_check
methods: [POST]
Мой AuthController:
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use App\Entity\User;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
class AuthController extends AbstractController
{
public function register(Request $request, UserPasswordEncoderInterface $encoder)
{
$em = $this->getDoctrine()->getManager();
$username = $request->request->get('_username');
$password = $request->request->get('_password');
$user = new User();
$user->setUsername($username);
$user->setEmail('');
$user->setPassword($encoder->encodePassword($user, $password));
$em->persist($user);
$em->flush();
return new Response(sprintf('User %s successfully created', $user->getUsername()));
}
public function api()
{
return new Response(sprintf('Logged in as %s', $this->getUser()->getUsername()));
}
}
Мой security.yaml:
security:
encoders:
App\Entity\User:
algorithm: bcrypt
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
providers:
entity_provider:
entity:
class: App\Entity\User
property: username
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
# main:
# anonymous: true
login:
pattern: ^/login
stateless: true
anonymous: true
json_login:
check_path: /login_check
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
register:
pattern: ^/register
stateless: true
anonymous: true
api:
pattern: ^/api
stateless: true
anonymous: false
provider: entity_provider
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
# activate different ways to authenticate
# http_basic: true
# https://symfony.com/doc/current/security.html#a-configuring-how-your-users-will-authenticate
# form_login: true
# https://symfony.com/doc/current/security/form_login_setup.html
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api, 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(JWT_PASSPHRASE)%'
Мой файл .env:
# This file defines all environment variables that the application needs.
# DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE.
# Use ".env.local" for local overrides during development.
# Use real environment variables when deploying to production.
# https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration
###> symfony/framework-bundle ###
APP_ENV=dev
APP_SECRET=0c700431af8a1ffea2c6a6636dda0d4e
#TRUSTED_PROXIES=127.0.0.1,127.0.0.2
#TRUSTED_HOSTS='^localhost|example\.com$'
###< symfony/framework-bundle ###
###> nelmio/cors-bundle ###
CORS_ALLOW_ORIGIN=^https?://localhost(:[0-9]+)?$
###< nelmio/cors-bundle ###
###> doctrine/doctrine-bundle ###
# Format described at http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
# For an SQLite database, use: "sqlite:///%kernel.project_dir%/var/data.db"
# Configure your db driver and server_version in config/packages/doctrine.yaml
DATABASE_URL=mysql://root:@127.0.0.1:3306/my_db_name
###< doctrine/doctrine-bundle ###
###> lexik/jwt-authentication-bundle ###
JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
JWT_PASSPHRASE=8cc34a18525a9c1c338424443bbdd185
###< lexik/jwt-authentication-bundle ###
Возможно, я что-то не так делаю,спасибо за помощь.