IgorPI
@IgorPI

Проблема в каждом втором запросе?

Коллеги, здравствуйте!

Возникла проблема в системе безопасности synfony (firewalls)

Посмотрите на гифку.
5dcc3df040dc6751050984.gif

По поводу анимации, при запросе я ожидаю ошибку и только её!


An exception occurred while executing 'INSERT INTO services (name, altName, lft, lvl, rgt, root_id, parent_id) VALUES (?, ?, ?, ?, ?, ?, ?)' with params [\"A\", null, 0, 0, 0, null, 26]:\n\nSQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'A-26' for key 'services_unique_name_parent_id'


Описание проблемы.
Когда я выполняю запрос, в первый раз все срабатывает, стоит мне тут же выполнить повторный запрос, тут же возникает ошибка There is no user provider for user \"App\\Entity\\User\".

И так далее, получается что через раз происходит ошибка.

Вот некоторые параметры.

config/admin/packages/security.yaml
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
    providers:
        in_memory: { memory: null }

    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false

        get_access_token:
            pattern: ^/security.getAccessToken
            guard:
                entry_point: App\Security\CreateAccessToken

        check_access_token:
            pattern: ^/security.checkAccessToken
            guard:
                entry_point: App\Security\CheckAccessToken

        main:
            anonymous: ~
            guard:
                authenticators :
                    - App\Security\TokenAuthenticator


src/Security/TokenAuthenticator.php
TokenAuthenticator

<?php

namespace App\Security;

use App\Entity\User;
use App\Exception\Api\EAuthorisationError;
use App\Exception\Api\ExceptionInvalidRequest;
use Doctrine\ORM\EntityManagerInterface;
use Jose\Component\Core\AlgorithmManager;
use Jose\Component\Core\AlgorithmManagerFactory;
use Jose\Component\Core\JWK;
use Jose\Component\Signature\JWSVerifier;
use Jose\Component\Signature\Serializer\CompactSerializer;
use Jose\Component\Signature\Serializer\JWSSerializerManager;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;

/**
 * Class TokenAuthenticator 
 * @package App\Security
 */
class TokenAuthenticator extends AbstractGuardAuthenticator
{

    /** @var AlgorithmManagerFactory */
    private $algorithmManagerFactory;

    /** @var AlgorithmManager */
    private $algorithmManager;

    /** @var EntityManagerInterface */
    private $entityManager;
    /** @var string */
    private $ip;


    /**
     * AppAuthAuthenticator constructor.
     * @param EntityManagerInterface $entityManager
     * @param AlgorithmManagerFactory $algorithmManagerFactory
     */
    public function __construct(EntityManagerInterface $entityManager, AlgorithmManagerFactory $algorithmManagerFactory)
    {
        $this->entityManager = $entityManager;
        $this->algorithmManagerFactory = $algorithmManagerFactory;
        $this->algorithmManager = $this->algorithmManagerFactory->create(["RS256", "HS512"]);
        $this->ip = $_SERVER['REMOTE_ADDR'];
    }

    /**
     * @param string $token
     * @return bool
     * @throws EAuthorisationError
     */
    public function verification(string $token): bool
    {
        $jwk = new JWK([
            "kty" => "oct",
            "k" => base64_encode($_ENV["JWT_KEY"] . $this->ip),
        ]);

        // The serializer manager. We only use the JWS Compact Serialization Mode.
        $serializerManager = new JWSSerializerManager([
            new CompactSerializer(),
        ]);

        $jwsVerifier = new JWSVerifier($this->algorithmManager);

        try {
            $jws = $serializerManager->unserialize($token);
        }catch (\Exception $exception) {
            throw new EAuthorisationError("Не удалось проверить токен");
        }

        return $jwsVerifier->verifyWithKey($jws, $jwk, 0);
    }

    /**
     * @param Request $request
     * @return bool|void
     */
    public function supports(Request $request)
    {
        return true;
    }


    /**
     * @param Request $request
     * @return array
     * @throws EAuthorisationError
     */
    public function getCredentials(Request $request)
    {
        try {
            preg_match('/Bearer\s+(.*?)$/m', $request->headers->get("authorization"), $match);
            return [
                "token" => $match[1],
            ];
        }catch (\Exception $exception) {
            throw new EAuthorisationError("Не верный токен");
        }
    }



    /**
     * Return a UserInterface object based on the credentials.
     *
     * The *credentials* are the return value from getCredentials()
     *
     * You may throw an AuthenticationException if you wish. If you return
     * null, then a UsernameNotFoundException is thrown for you.
     *
     * @param mixed $credentials
     *
     * @param UserProviderInterface $userProvider
     * @return User|UserProviderInterface
     */
    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        return new User();
    }

    /**
     * Returns true if the credentials are valid.
     *
     * If any value other than true is returned, authentication will
     * fail. You may also throw an AuthenticationException if you wish
     * to cause authentication to fail.
     *
     * The *credentials* are the return value from getCredentials()
     *
     * @param mixed $credentials
     *
     * @return bool
     *
     * @throws AuthenticationException
     * @throws EAuthorisationError
     */
    public function checkCredentials($credentials, UserInterface $user)
    {
        if ($this->verification($credentials["token"])) {
            return true;
        }

        throw new EAuthorisationError("Не верный токен");
    }

    /**
     * Called when authentication executed, but failed (e.g. wrong username password).
     *
     * This should return the Response sent back to the user, like a
     * RedirectResponse to the login page or a 403 response.
     *
     * If you return null, the request will continue, but the user will
     * not be authenticated. This is probably not what you want to do.
     *
     * @return Response|null
     */
    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
    {
        return null;
    }

    /**
     * Called when authentication executed and was successful!
     *
     * This should return the Response sent back to the user, like a
     * RedirectResponse to the last page they visited.
     *
     * If you return null, the current request will continue, and the user
     * will be authenticated. This makes sense, for example, with an API.
     *
     * @param string $providerKey The provider (i.e. firewall) key
     *
     * @return Response|null
     */
    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
        return null;
    }

    /**
     * Does this method support remember me cookies?
     *
     * Remember me cookie will be set if *all* of the following are met:
     *  A) This method returns true
     *  B) The remember_me key under your firewall is configured
     *  C) The "remember me" functionality is activated. This is usually
     *      done by having a _remember_me checkbox in your form, but
     *      can be configured by the "always_remember_me" and "remember_me_parameter"
     *      parameters under the "remember_me" firewall key
     *  D) The onAuthenticationSuccess method returns a Response object
     *
     * @return bool
     */
    public function supportsRememberMe()
    {
        // TODO: Implement supportsRememberMe() method.
    }

    /**
     * Returns a response that directs the user to authenticate.
     *
     * This is called when an anonymous request accesses a resource that
     * requires authentication. The job of this method is to return some
     * response that "helps" the user start into the authentication process.
     *
     * Examples:
     *
     * - For a form login, you might redirect to the login page
     *
     *     return new RedirectResponse('/login');
     *
     * - For an API token authentication system, you return a 401 response
     *
     *     return new Response('Auth header required', 401);
     *
     * @param Request $request The request that resulted in an AuthenticationException
     * @param AuthenticationException $authException The exception that started the authentication process
     *
     * @return Response
     * @throws EAuthorisationError
     * @throws ExceptionInvalidRequest
     */
    public function start(Request $request, AuthenticationException $authException = null)
    {
        if (!$this->supports($request)) {
            throw new ExceptionInvalidRequest("Требуется параметр [token]!", $request);
        }

        $credentials = $this->getCredentials($request);

        if ($this->verification($credentials["token"])) {
            return new Response("");
        }

        throw new EAuthorisationError("Token is not valid!");
    }
}

  • Вопрос задан
  • 357 просмотров
Решения вопроса 1
BoShurik
@BoShurik Куратор тега Symfony
Symfony developer
Вам нужно либо реализовать примитивный провайдер, который будет возвращать new User() вместо вашего кода
public function getUser($credentials, UserProviderInterface $userProvider)
{
    return new User();
}

либо добавить stateless: true
Проблема в ContextListener который
ContextListener manages the SecurityContext persistence through a session.

Он ожидает, что в сессии находится токен пользователя и пытается его восстановить из провайдера, которого у вас нет.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы