Используйте League OAuthServerBundle с настраиваемым аутентификатором в Symfony 6
Я пытался использовать пакет серверов League Oauth2 с настраиваемым аутентификатором, но моя конфигурация в нем кажется неправильной.
Пакет OAuth2 должен прослушивать
/authorize
route, а затем перенаправить на мою страницу входа, управляемую моим 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.');
}
}