Используйте League OAuthServerBundle с настраиваемым аутентификатором в Symfony 6

Я пытался использовать пакет серверов League Oauth2 с настраиваемым аутентификатором, но моя конфигурация в нем кажется неправильной.

Пакет OAuth2 должен прослушивать /authorizeroute, а затем перенаправить на мою страницу входа, управляемую моим custom. После входа в систему должно пройти обычное перенаправление OAuth с кодом авторизации. Это не работает, и я думаю, что причина в.

Пробовал несколько комбинаций в комплектации. Например, если я определяю и entry_point: oauth2 в mainбрандмауэр, страница входа не отображается, но перенаправление работает. Если я определю oauth2: true и entry_point: Ntrx\UserBundle\Security\LoginFormAuthenticator, страница входа отображается, но перенаправление не работает. Таким образом, сочетание того и другого, кажется, является проблемой.

Спасибо за помощь!

В security.yaml:

      security:
    password_hashers:
        App\Entity\User:
            algorithm: auto
            cost: 4
        Symfony\Component\Security\Core\User\User: plaintext

    enable_authenticator_manager: true

    # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
    providers:
        app_user_provider:
            entity:
                class: App\Entity\User
                property: email

    firewalls:
        dev:
            pattern: ^/(_wdt|css|images|js)/
            security: false

        api_doc:
            pattern: ^/((/api/doc)|_profiler)/
            security: true
            http_basic:
                provider: docs_basic_auth

        api_token:
            pattern: ^/oauth/token
            security: false

        api:
            pattern: ^/api
            security: true
            stateless: true
            provider: app_user_provider
            oauth2: true
            logout:
                path: ntrx_userbundle_logout
                target: application_controller_home
                invalidate_session: true

        main:
            stateless: true
            provider: app_user_provider
            custom_authenticators: [Ntrx\UserBundle\Security\LoginFormAuthenticator]
            logout:
                path: ntrx_userbundle_logout
                target: application_controller_home
                invalidate_session: true

    # Easy way to control access for large sections of your site
    # Note: Only the *first* access control that matches will be used
    access_control:
        - { path: ^/oauth/authorize, roles: IS_AUTHENTICATED_REMEMBERED }
        - { path: ^/api, roles: IS_AUTHENTICATED_FULLY }

В LoginFormAuthenticator:

      <?php

namespace Ntrx\UserBundle\Security;

use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Http\Authenticator\AbstractLoginFormAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Util\TargetPathTrait;


class LoginFormAuthenticator extends AbstractLoginFormAuthenticator
{
    use TargetPathTrait;

    public const LOGIN_ROUTE = 'ntrx_userbundle_login';

    private EntityManagerInterface $entityManager;
    private UrlGeneratorInterface $urlGenerator;
    private string $userClass;


    public function __construct(EntityManagerInterface $entityManager, UrlGeneratorInterface $urlGenerator, ParameterBagInterface $params) {
        $this->entityManager = $entityManager;
        $this->urlGenerator = $urlGenerator;
        $this->userClass = $params->get('ntrx_user.user_class');
    }


    protected function getLoginUrl(Request $request): string {
        return $this->urlGenerator->generate(self::LOGIN_ROUTE);
    }


    public function supports(Request $request): bool {
        return $request->isMethod('POST') && $this->getLoginUrl($request) === $request->getPathInfo();
    }


    public function authenticate(Request $request): Passport {
        $credentials = $this->getCredentials($request);

        $passport = new Passport(
            new UserBadge($credentials['email'], function ($userIdentifier) {
                $user = $this->entityManager->getRepository($this->userClass)->findOneBy(['email' => $userIdentifier]);
                return $user;
            }),
            new PasswordCredentials($credentials['password']),
            [new RememberMeBadge(), new CsrfTokenBadge('authenticate', $credentials['csrf_token'])]
        );

        return $passport;
    }


    public function createToken(Passport $passport, string $firewallName): TokenInterface {
        return new UsernamePasswordToken($passport->getUser(), $firewallName, $passport->getUser()->getRoles());
    }


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

        return new RedirectResponse($this->urlGenerator->generate(self::LOGIN_ROUTE));
    }


    public function onAuthenticationFailure(Request $request, AuthenticationException $exception): Response {
        dd('failure', $exception);
    }


    public function start(Request $request, AuthenticationException $authException = null): Response {
        $url = $this->getLoginUrl($request);
        return new RedirectResponse($url);
    }


    public function isInteractive(): bool {
        return true;
    }


    /*
     * Private methods
     */
    private function getCredentials(Request $request): array {
        $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;
    }
}

В LoginController:

      <?php

namespace Ntrx\UserBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;


/**
 * Login controller.
 */
class LoginController extends AbstractController
{
    /**
     * @Route("/login", name="ntrx_userbundle_login", methods={"GET", "POST"})
     */
    public function login(AuthenticationUtils $authenticationUtils, ParameterBagInterface $params): Response {
        if ($this->getUser()) {
            return $this->redirectToRoute($params->get('ntrx_user.login_redirect'));
        }

        // get the login error if there is one
        $error = $authenticationUtils->getLastAuthenticationError();
        // last username entered by the user
        $lastEmail = $authenticationUtils->getLastUsername();

        return $this->render('@NtrxUser/login/login.html.twig', ['last_email' => $lastEmail, 'error' => $error]);
    }

    /**
     * @Route("/logout", name="ntrx_userbundle_logout")
     */
    public function logout() {
        throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
    }
}

0 ответов

Другие вопросы по тегам