Also use authentication failure/success handlers in FormLoginAuthenticator

This commit is contained in:
Wouter de Jong 2020-04-06 14:00:37 +02:00
parent 0fe5083a3e
commit 9ea32c4ed3
6 changed files with 36 additions and 49 deletions

View File

@ -30,6 +30,7 @@ abstract class AbstractFactory implements SecurityFactoryInterface
'check_path' => '/login_check',
'use_forward' => false,
'require_previous_session' => false,
'login_path' => '/login',
];
protected $defaultSuccessHandlerOptions = [

View File

@ -100,12 +100,13 @@ class FormLoginFactory extends AbstractFactory implements AuthenticatorFactoryIn
public function createAuthenticator(ContainerBuilder $container, string $id, array $config, string $userProviderId): string
{
$authenticatorId = 'security.authenticator.form_login.'.$id;
$defaultOptions = array_merge($this->defaultSuccessHandlerOptions, $this->options);
$options = array_merge($defaultOptions, array_intersect_key($config, $defaultOptions));
$options = array_intersect_key($config, $this->options);
$container
->setDefinition($authenticatorId, new ChildDefinition('security.authenticator.form_login'))
->replaceArgument(1, new Reference($userProviderId))
->replaceArgument(2, $options);
->replaceArgument(2, new Reference($this->createAuthenticationSuccessHandler($container, $id, $config)))
->replaceArgument(3, new Reference($this->createAuthenticationFailureHandler($container, $id, $config)))
->replaceArgument(4, $options);
return $authenticatorId;
}

View File

@ -95,6 +95,8 @@
abstract="true">
<argument type="service" id="security.http_utils" />
<argument type="abstract">user provider</argument>
<argument type="abstract">authentication success handler</argument>
<argument type="abstract">authentication failure handler</argument>
<argument type="abstract">options</argument>
</service>

View File

@ -30,7 +30,7 @@ abstract class AbstractLoginFormAuthenticator extends AbstractAuthenticator impl
/**
* Return the URL to the login page.
*/
abstract protected function getLoginUrl(): string;
abstract protected function getLoginUrl(Request $request): string;
/**
* Override to change what happens after a bad username/password is submitted.
@ -41,7 +41,7 @@ abstract class AbstractLoginFormAuthenticator extends AbstractAuthenticator impl
$request->getSession()->set(Security::AUTHENTICATION_ERROR, $exception);
}
$url = $this->getLoginUrl();
$url = $this->getLoginUrl($request);
return new RedirectResponse($url);
}
@ -52,7 +52,7 @@ abstract class AbstractLoginFormAuthenticator extends AbstractAuthenticator impl
*/
public function start(Request $request, AuthenticationException $authException = null): Response
{
$url = $this->getLoginUrl();
$url = $this->getLoginUrl($request);
return new RedirectResponse($url);
}

View File

@ -16,13 +16,15 @@ use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
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\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
use Symfony\Component\Security\Http\HttpUtils;
use Symfony\Component\Security\Http\ParameterBagUtils;
use Symfony\Component\Security\Http\Util\TargetPathTrait;
/**
* @author Wouter de Jong <wouter@wouterj.nl>
@ -33,34 +35,32 @@ use Symfony\Component\Security\Http\Util\TargetPathTrait;
*/
class FormLoginAuthenticator extends AbstractLoginFormAuthenticator implements PasswordAuthenticatedInterface, CsrfProtectedAuthenticatorInterface
{
use TargetPathTrait;
private $options;
private $httpUtils;
private $userProvider;
private $successHandler;
private $failureHandler;
private $options;
public function __construct(HttpUtils $httpUtils, UserProviderInterface $userProvider, array $options)
public function __construct(HttpUtils $httpUtils, UserProviderInterface $userProvider, AuthenticationSuccessHandlerInterface $successHandler, AuthenticationFailureHandlerInterface $failureHandler, array $options)
{
$this->httpUtils = $httpUtils;
$this->userProvider = $userProvider;
$this->successHandler = $successHandler;
$this->failureHandler = $failureHandler;
$this->options = array_merge([
'username_parameter' => '_username',
'password_parameter' => '_password',
'csrf_parameter' => '_csrf_token',
'csrf_token_id' => 'authenticate',
'check_path' => '/login_check',
'post_only' => true,
'always_use_default_target_path' => false,
'default_target_path' => '/',
'login_path' => '/login',
'target_path_parameter' => '_target_path',
'use_referer' => false,
'csrf_parameter' => '_csrf_token',
'csrf_token_id' => 'authenticate',
], $options);
$this->userProvider = $userProvider;
}
protected function getLoginUrl(): string
protected function getLoginUrl(Request $request): string
{
return $this->options['login_path'];
return $this->httpUtils->generateUri($request, $this->options['login_path']);
}
public function supports(Request $request): bool
@ -122,36 +122,13 @@ class FormLoginAuthenticator extends AbstractLoginFormAuthenticator implements P
return new UsernamePasswordToken($user, null, $providerKey, $user->getRoles());
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $providerKey): Response
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $providerKey): ?Response
{
return $this->httpUtils->createRedirectResponse($request, $this->determineTargetUrl($request, $providerKey));
return $this->successHandler->onAuthenticationSuccess($request, $token);
}
private function determineTargetUrl(Request $request, string $providerKey)
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): Response
{
if ($this->options['always_use_default_target_path']) {
return $this->options['default_target_path'];
}
if ($targetUrl = ParameterBagUtils::getRequestParameterValue($request, $this->options['target_path_parameter'])) {
return $targetUrl;
}
if ($targetUrl = $this->getTargetPath($request->getSession(), $providerKey)) {
$this->removeTargetPath($request->getSession(), $providerKey);
return $targetUrl;
}
if ($this->options['use_referer'] && $targetUrl = $request->headers->get('Referer')) {
if (false !== $pos = strpos($targetUrl, '?')) {
$targetUrl = substr($targetUrl, 0, $pos);
}
if ($targetUrl && $targetUrl !== $this->httpUtils->generateUri($request, $this->options['login_path'])) {
return $targetUrl;
}
}
return $this->options['default_target_path'];
return $this->failureHandler->onAuthenticationFailure($request, $exception);
}
}

View File

@ -17,17 +17,23 @@ use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
use Symfony\Component\Security\Http\Authenticator\FormLoginAuthenticator;
use Symfony\Component\Security\Http\HttpUtils;
class FormLoginAuthenticatorTest extends TestCase
{
private $userProvider;
private $successHandler;
private $failureHandler;
private $authenticator;
protected function setUp(): void
{
$this->userProvider = $this->createMock(UserProviderInterface::class);
$this->successHandler = $this->createMock(AuthenticationSuccessHandlerInterface::class);
$this->failureHandler = $this->createMock(AuthenticationFailureHandlerInterface::class);
}
/**
@ -123,7 +129,7 @@ class FormLoginAuthenticatorTest extends TestCase
private function setUpAuthenticator(array $options = [])
{
$this->authenticator = new FormLoginAuthenticator(new HttpUtils(), $this->userProvider, $options);
$this->authenticator = new FormLoginAuthenticator(new HttpUtils(), $this->userProvider, $this->successHandler, $this->failureHandler, $options);
}
private function createSession()