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 ...@@ -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_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
JWT_PASSPHRASE= JWT_PASSPHRASE=
JWT_TTL=3600
###< lexik/jwt-authentication-bundle ### ###< lexik/jwt-authentication-bundle ###
###> symfony/mailer ### ###> symfony/mailer ###
......
...@@ -2,4 +2,4 @@ lexik_jwt_authentication: ...@@ -2,4 +2,4 @@ lexik_jwt_authentication:
secret_key: '%env(resolve:JWT_SECRET_KEY)%' secret_key: '%env(resolve:JWT_SECRET_KEY)%'
public_key: '%env(resolve:JWT_PUBLIC_KEY)%' public_key: '%env(resolve:JWT_PUBLIC_KEY)%'
pass_phrase: '%env(JWT_PASSPHRASE)%' pass_phrase: '%env(JWT_PASSPHRASE)%'
token_ttl: 3600 token_ttl: 604800
...@@ -9,6 +9,7 @@ parameters: ...@@ -9,6 +9,7 @@ parameters:
from_email: '%env(MAILER_ADDRESS)%' from_email: '%env(MAILER_ADDRESS)%'
# Директория сохранения файлов # Директория сохранения файлов
images_directory: '%kernel.project_dir%/public/uploads/user_images' images_directory: '%kernel.project_dir%/public/uploads/user_images'
jwt_ttl: '%env(JWT_TTL)%'
services: services:
# default configuration for services in *this* file # default configuration for services in *this* file
...@@ -25,6 +26,10 @@ services: ...@@ -25,6 +26,10 @@ services:
- '../src/Entity/' - '../src/Entity/'
- '../src/Kernel.php' - '../src/Kernel.php'
App\Service\RedisToken:
arguments:
$ttl: '%jwt_ttl%'
App\Service\Action\Classes\SaveImage: App\Service\Action\Classes\SaveImage:
arguments: arguments:
$targetDirectory: '%images_directory%' $targetDirectory: '%images_directory%'
...@@ -74,6 +79,16 @@ services: ...@@ -74,6 +79,16 @@ services:
tags: tags:
- { name: kernel.event_listener, event: lexik_jwt_authentication.on_jwt_expired, method: onJWTExpired } - { 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: gesdinet.jwtrefreshtoken.send_token:
class: App\Listeners\JwtRefreshListener class: App\Listeners\JwtRefreshListener
arguments: arguments:
......
...@@ -3,17 +3,27 @@ ...@@ -3,17 +3,27 @@
namespace App\Listeners; namespace App\Listeners;
use App\Entity\User; use App\Entity\User;
use App\Service\RedisToken;
use App\Service\Response\Classes\TokenResponse; use App\Service\Response\Classes\TokenResponse;
use JsonException; use JsonException;
use Lexik\Bundle\JWTAuthenticationBundle\Event\AuthenticationFailureEvent; use Lexik\Bundle\JWTAuthenticationBundle\Event\AuthenticationFailureEvent;
use Lexik\Bundle\JWTAuthenticationBundle\Event\AuthenticationSuccessEvent; 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\JWTExpiredEvent;
use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTInvalidEvent; use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTInvalidEvent;
use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTNotFoundEvent; use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTNotFoundEvent;
use Psr\Cache\InvalidArgumentException;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
class JwtListener class JwtListener
{ {
public function __construct(
private RedisToken $redisToken,
)
{
}
/** /**
* @param AuthenticationSuccessEvent $event * @param AuthenticationSuccessEvent $event
* *
...@@ -95,5 +105,41 @@ class JwtListener ...@@ -95,5 +105,41 @@ class JwtListener
$response->setStatusCode(Response::HTTP_FORBIDDEN); $response->setStatusCode(Response::HTTP_FORBIDDEN);
$event->setResponse($response->getResponse()); $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