Вчера мы увидели, как защитить наш API с помощью токена и как использовать токен в VueJs. Сегодня мы увидим, как аутентифицировать пользователя в нашем приложении в контексте SPA с помощью VueJS .
Продолжим шаг за шагом.
1. Безопасность с Symfony
A) Создать объект пользователя
<?php namespace App\Entity; ... /** * @ApiResource( * itemOperations={ * "get"={ * "path"="/users/{id}", * "swagger_context"={ * "tags"={"Account"} * } * } * }, * collectionOperations={ * "post"={ * "path"="/users", * "method"="POST", * "swagger_context"={ * "tags"={"Account"}, * "summary"={"Create a new account"} * } * }, * "get"={ * "path"="/users", * "method"="GET", * "swagger_context"={ * "tags"={"Account"} * } * } * }, * ) * * @ORM\Entity(repositoryClass="App\Repository\UserAccountRepository") */ final class UserAccount implements UserInterface { /** * @ORM\Id() * @ORM\GeneratedValue() * @ORM\Column(type="integer") */ private $id; /** * @ORM\Column(type="string", length=180) */ private $firstName; /** * @ORM\Column(type="string", length=180) */ private $lastName; /** * @ORM\Column(type="string", length=180, unique=true) */ private $email; /** * @ORM\Column(type="json") */ private $roles = []; /** * @var string The hashed password * @ORM\Column(type="string") */ private $password; /** * @ORM\Column(type="string", unique=true, nullable=true) */ private $apiToken; public function getId(): ?int { return $this->id; } /** * A visual identifier that represents this user. * * @see UserInterface */ public function getUsername(): string { return (string)$this->email; } public function getEmail(): string { return (string)$this->email; } public function setEmail(string $email): self { $this->email = $email; return $this; } /** * @see UserInterface */ public function getRoles(): array { $roles = $this->roles; // guarantee every user at least has ROLE_USER $roles[] = 'ROLE_USER'; return array_unique($roles); } public function setRoles(array $roles): self { $this->roles = $roles; return $this; } /** * @see UserInterface */ public function getPassword(): string { return (string)$this->password; } public function setPassword(string $password): self { $this->password = $password; return $this; } /** * @see UserInterface */ public function getSalt() { // not needed when using the "bcrypt" algorithm in security.yaml } /** * @see UserInterface */ public function eraseCredentials() { // If you store any temporary, sensitive data on the user, clear it here // $this->plainPassword = null; } public function getApiToken(): ?string { return $this->apiToken; } public function setApiToken($apiToken): void { $this->apiToken = $apiToken; } public function getFirstName() { return $this->firstName; } public function setFirstName($firstName): void { $this->firstName = $firstName; } public function getLastName() { return $this->lastName; } public function setLastName($lastName): void { $this->lastName = $lastName; } }
Б) Создать аутентификатор
<?php namespace App\Security; ... class JsonAuthenticator extends AbstractFormLoginAuthenticator { use TargetPathTrait; private $entityManager; private $router; private $csrfTokenManager; private $passwordEncoder; public function __construct( EntityManagerInterface $entityManager, RouterInterface $router, CsrfTokenManagerInterface $csrfTokenManager, UserPasswordEncoderInterface $passwordEncoder ) { $this->entityManager = $entityManager; $this->router = $router; $this->csrfTokenManager = $csrfTokenManager; $this->passwordEncoder = $passwordEncoder; } public function supports(Request $request) { return 'app_account' === $request->attributes->get('_route') && $request->isMethod('POST'); } public function getCredentials(Request $request) { if ($request->getContentType() !== 'json') { throw new BadRequestHttpException(); } $data = json_decode($request->getContent(), true); $credentials = [ 'email' => $data['email'], 'password' => $data['password'], 'csrf_token' => $data['_csrf_token'], ]; $request->getSession()->set( Security::LAST_USERNAME, $credentials['email'] ); return $credentials; } public function getUser($credentials, UserProviderInterface $userProvider) { $token = new CsrfToken('authenticate', $credentials['csrf_token']); if (!$this->csrfTokenManager->isTokenValid($token)) { throw new InvalidCsrfTokenException(); } $user = $this->entityManager->getRepository(UserAccount::class)->findOneBy(['email' => $credentials['email']]); if (!$user) { // fail authentication with a custom error throw new CustomUserMessageAuthenticationException('Email could not be found.'); } return $user; } public function checkCredentials($credentials, UserInterface $user) { return $this->passwordEncoder->isPasswordValid($user, $credentials['password']); } public function onAuthenticationFailure(Request $request, AuthenticationException $exception) { return new JsonResponse($exception->getMessage()); } public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey) { if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) { return new RedirectResponse($targetPath); } return new JsonResponse('authenticated'); } }
Продолжить чтение: https://stefanoalletti.wordpress.com/2019/08/05/authentication-with-vuejs-using-symfony-and-api-platform-part-2/