Symfony 5 межсетевой экран с JWT блокирует доступ к некоторым маршрутам без входа в систему - PullRequest
0 голосов
/ 14 февраля 2020

Я использую LexikJWTAuthenticationBundle , чтобы управлять созданием токена и всем этим джазом.

Мой security.yaml файл выглядит следующим образом (удалены не релевантные вещи):

security:
    encoders:
        App\Entity\User:
            algorithm: auto
    providers:
        app_user_provider:
            entity:
                class: App\Entity\User
                property: email
    firewalls:
        login:
            pattern:  ^/api/login
            stateless: true
            anonymous: true
            json_login:
                check_path: /api/login_check
                success_handler: lexik_jwt_authentication.handler.authentication_success
                failure_handler: lexik_jwt_authentication.handler.authentication_failure
        api:
            pattern:   ^/api
            stateless: true
            guard:
                authenticators:
                    - lexik_jwt_authentication.jwt_token_authenticator
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            anonymous: lazy
            guard:
                authenticators:
                    - App\Security\LoginFormAuthenticator
            logout:
                path: logout
                target: /
                invalidate_session: true
            remember_me:
                secret:   '%kernel.secret%'
                lifetime: 604800 # 1 week in seconds
                path:     /
                always_remember_me: true

    access_control:
         - { path: ^/admin, roles: ROLE_ADMIN }
         - { path: ^/profile, roles: ROLE_USER }
         - { path: ^/login$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
         - { path: ^/register, roles: IS_AUTHENTICATED_ANONYMOUSLY }
         - { path: ^/user, roles: ROLE_USER }
         - { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
         - { path: ^/api,       roles: IS_AUTHENTICATED_FULLY }

Что касается JWT, то он работает нормально

Выполнение простого скручивания

$ curl -X POST -H "Content-Type: application/json" http://localhost/api/\login_check -d '{"username":"someusername","password":"somepassword!"}'

Возвращает токен:

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                             Dload  Upload   Total   Spent    Left  Speed
100   910  100   852  100    58    376     25  0:00:02  0:00:02 --:--:-- 376
{"token":"6wvL6E..."}

Проблема в том, что любой другой маршрут 302 отвечает немедленно перенаправляет на /

Request URL: http://localhost/register
Request Method: GET
Status Code: 302 Found
Remote Address: [::1]:80
Referrer Policy: no-referrer-when-downgrade

Довольно ясно, что я где-то что-то упустил. Но я не могу найти. Насколько я понимаю, конфиги кажутся хорошими.

Возможно, кто-то с лучшим глазом (и лучшим опытом), чем я, может помочь мне.

Я могу только предположить, что JWT ожидает, что у X маршрута есть токен, но я явно внесу в белый список определенные пути в access_control.

Удаление пакета и отмена любых изменений приведут к тому, что все маршруты будут работать так, как ожидается.

При Дальнейшее расследование, кажется, вступает в противоречие с обычаем LoginFormAuthenticator

<?php

namespace App\Security;

use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;
use Symfony\Component\Security\Guard\PasswordAuthenticatedInterface;
use Symfony\Component\Security\Http\Util\TargetPathTrait;

class LoginFormAuthenticator extends AbstractFormLoginAuthenticator implements PasswordAuthenticatedInterface
{
    use TargetPathTrait;

    private $entityManager;
    private $urlGenerator;
    private $csrfTokenManager;
    private $passwordEncoder;

    public function __construct(EntityManagerInterface $entityManager, UrlGeneratorInterface $urlGenerator, CsrfTokenManagerInterface $csrfTokenManager, UserPasswordEncoderInterface $passwordEncoder)
    {
        $this->entityManager = $entityManager;
        $this->urlGenerator = $urlGenerator;
        $this->csrfTokenManager = $csrfTokenManager;
        $this->passwordEncoder = $passwordEncoder;
    }

    public function supports(Request $request)
    {
        return 'login_user' === $request->attributes->get('_route') && $request->isMethod('POST');
    }

    public function getCredentials(Request $request)
    {
        $credentials = [
            'email' => $request->request->get('email'),
            'password' => $request->request->get('password'),
            'csrf_token' => $request->request->get('_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(User::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']);
    }

    /**
     * Used to upgrade (rehash) the user's password automatically over time.
     */
    public function getPassword($credentials): ?string
    {
        return $credentials['password'];
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
        if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
            return new RedirectResponse($targetPath);
        }

        return new RedirectResponse($this->urlGenerator->generate('index', [], RouterInterface::ABSOLUTE_URL));
    }

    protected function getLoginUrl()
    {
        return $this->urlGenerator->generate('login_user');
    }
}

Почему именно это происходит, я не уверен, так как их охранники находятся в отдельных брандмауэрах с четко различимыми маршрутами. Насколько я могу судить, даже не тот префикс.

1 Ответ

0 голосов
/ 15 февраля 2020

Таким образом, в вашей конфигурации у вас есть главный брандмауэр с пользовательским LoginFormAuthenticator , в котором я не вижу всего смысла, когда вам не нужен аутентификатор для входа в систему при использовании LexikJWTAuthenticationBundle .

Также я заметил, что если вы сделаете запрос, например, к http://localhost/register, он будет использовать LoginFormAuthenticator , который не будет поддерживать запрос, потому что в его функции support () вы объявили, что принимаете определенный маршрут.

return 'login_user' === $request->attributes->get('_route') ....

Другая проблема, которую вы можете устранить, - это функция onAuthenticationSuccess, особенно у меня есть подозрения по поводу этой строки.

return new RedirectResponse($this->urlGenerator->generate('index', [], RouterInterface::ABSOLUTE_URL));

Я бы предположил, что у вас есть маршрут, который называется index и имеет путь "/", и если вы используете route.yaml, он будет выглядеть примерно так. (У вас есть тот же принцип и в других типах)

index:
    path: /
    controller: App\Controller\ACMEController::fooFunction

Таким образом, вы в основном говорите своему аутентификатору перенаправить на «/». Это было бы моим другим предположением. Я надеюсь, что я был полезным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...