Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • i.vasilenko/symfony-trainee
1 result
Show changes
Commits on Source (2)
......@@ -7,6 +7,7 @@ DATABASE_URL="postgresql://user:password@db:5432/symf?serverVersion=16&charset=u
JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
JWT_PASSPHRASE=
JWT_TTL=3600
###< lexik/jwt-authentication-bundle ###
###> symfony/mailer ###
......
......@@ -2,4 +2,4 @@ lexik_jwt_authentication:
secret_key: '%env(resolve:JWT_SECRET_KEY)%'
public_key: '%env(resolve:JWT_PUBLIC_KEY)%'
pass_phrase: '%env(JWT_PASSPHRASE)%'
token_ttl: 3600
token_ttl: 604800
......@@ -9,6 +9,7 @@ parameters:
from_email: '%env(MAILER_ADDRESS)%'
# Директория сохранения файлов
images_directory: '%kernel.project_dir%/public/uploads/user_images'
jwt_ttl: '%env(JWT_TTL)%'
services:
# default configuration for services in *this* file
......@@ -25,6 +26,10 @@ services:
- '../src/Entity/'
- '../src/Kernel.php'
App\Service\RedisToken:
arguments:
$ttl: '%jwt_ttl%'
App\Service\Action\Classes\SaveImage:
arguments:
$targetDirectory: '%images_directory%'
......@@ -74,6 +79,16 @@ services:
tags:
- { name: kernel.event_listener, event: lexik_jwt_authentication.on_jwt_expired, method: onJWTExpired }
acme_api.event.jwt_created_listener:
class: App\Listeners\JwtListener
tags:
- { name: kernel.event_listener, event: lexik_jwt_authentication.on_jwt_created, method: onJWTCreated }
acme_api.event.jwt_decoded_listener:
class: App\Listeners\JwtListener
tags:
- { name: kernel.event_listener, event: lexik_jwt_authentication.on_jwt_decoded, method: onJWTDecoded }
gesdinet.jwtrefreshtoken.send_token:
class: App\Listeners\JwtRefreshListener
arguments:
......
......@@ -3,17 +3,27 @@
namespace App\Listeners;
use App\Entity\User;
use App\Service\RedisToken;
use App\Service\Response\Classes\TokenResponse;
use JsonException;
use Lexik\Bundle\JWTAuthenticationBundle\Event\AuthenticationFailureEvent;
use Lexik\Bundle\JWTAuthenticationBundle\Event\AuthenticationSuccessEvent;
use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTCreatedEvent;
use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTDecodedEvent;
use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTExpiredEvent;
use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTInvalidEvent;
use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTNotFoundEvent;
use Psr\Cache\InvalidArgumentException;
use Symfony\Component\HttpFoundation\Response;
class JwtListener
{
public function __construct(
private RedisToken $redisToken,
)
{
}
/**
* @param AuthenticationSuccessEvent $event
*
......@@ -95,5 +105,41 @@ class JwtListener
$response->setStatusCode(Response::HTTP_FORBIDDEN);
$event->setResponse($response->getResponse());
$this->redisToken->clearOld();
}
/**
* @param JWTCreatedEvent $event
*
* @return void
*
* @throws InvalidArgumentException
*/
public function onJWTCreated(JWTCreatedEvent $event): void
{
$payload = $event->getData();
$this->redisToken->set($payload['username']);
}
/**
* @param JWTDecodedEvent $event
*
* @return void
*
* @throws InvalidArgumentException
*/
public function onJWTDecoded(JWTDecodedEvent $event): void
{
if ($event->isValid()) {
$payload = $event->getPayload();
if (!$this->redisToken->check($payload['username'])) {
$event->markAsInvalid();
}
$event->setPayload($payload);
}
}
}
\ No newline at end of file
<?php
namespace App\Service;
use App\Entity\User;
use App\Redis\Redis;
use Doctrine\Persistence\ManagerRegistry;
use Psr\Cache\InvalidArgumentException;
class RedisToken
{
private int $ttl;
private Redis $redis;
public function __construct(
int $ttl,
private ManagerRegistry $doctrine
)
{
$this->ttl = $ttl;
$this->redis = Redis::getInstance();
}
/**
* Установка времени создания токена
*
* @param string $username
*
* @return int
*
* @throws InvalidArgumentException
*/
public function set(string $username): int
{
$time = time();
$this->redis->set($this->format($this->getId($username)), $time);
return $time;
}
/**
* Получение времени
*
* @param string $username
*
* @return bool
*
* @throws InvalidArgumentException
*/
public function check(string $username): bool
{
$time = $this->redis->get($this->format($this->getId($username)));
if (!$time) {
$time = $this->set($username);
}
$time += $this->ttl;
$now = time();
if ($now > $time) {
$this->delete($username);
return false;
}
$this->set($username);
return true;
}
/**
* Удаление времени
*
* @param string $username
*
* @return void
*/
public function delete(string $username): void
{
if ($id = $this->getId($username)) {
$this->redis->delete($this->format($id));
}
}
/**
* Очистка прошедшего времени
*
* @return void
*
* @throws InvalidArgumentException
*/
public function clearOld(): void
{
$now = time();
$users = $this->doctrine->getManager()->getRepository(User::class)->findAll();
foreach ($users as $user) {
$code = $this->format($user->getId());
if ($this->redis->has($code)) {
$time = $this->redis->get($code);
if ($now > $time) {
$this->redis->delete($code);
}
}
}
}
private function format(int $id): string
{
return 'token_ttl_' . $id;
}
private function getId(string $username): ?int
{
$user = $this->doctrine->getManager()->getRepository(User::class)->findOneBy(['email' => $username]);
if ($user) {
return $user->getId();
}
return null;
}
}
\ No newline at end of file