<?php
namespace App\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
class RequestSignListener implements EventSubscriberInterface
{
/**
* @var string
*/
private string $clientSecret;
public function __construct(string $clientSecret)
{
$this->clientSecret = $clientSecret;
}
/**
* Проверка подписи запроса
* @phpstan-ignore-next-line
*/
public function onRequest(RequestEvent $event)
{
if (!$event->isMainRequest()) {
return;
}
$request = $event->getRequest();
if ('/api/' === substr($request->getPathInfo(), 0, 5)) {
if (!$this->isRequestFromVKSignedCorrectly($request)) {
$event->setResponse($this->createAccessDeniedResponse('Access denied.'));
}
}
}
public static function getSubscribedEvents(): array
{
return [
KernelEvents::REQUEST => 'onRequest',
];
}
private function isRequestFromVKSignedCorrectly(Request $request): bool
{
$queryParams = [];
parse_str(parse_url($request->getRequestUri(), PHP_URL_QUERY), $queryParams);
$signParams = [];
foreach ($queryParams as $name => $value) {
if (strpos($name, 'vk_') !== 0) {
continue;
}
$signParams[$name] = $value;
}
if (empty($signParams) || !isset($queryParams['sign'])) {
return false;
}
ksort($signParams);
$signParamsQuery = http_build_query($signParams);
$sign = rtrim(strtr(base64_encode(hash_hmac(
'sha256',
$signParamsQuery,
$this->clientSecret,
true
)), '+/', '-_'), '=');
return $sign === $queryParams['sign'];
}
private function createAccessDeniedResponse(string $message): JsonResponse
{
$response = new JsonResponse([
'errorMsg' => $message,
'success' => false,
]);
$response->setStatusCode(403);
$response->headers->set('Content-type', 'application/json; charset=UTF-8');
return $response;
}
}