Файл: symfony-2.7/src/Symfony/Component/Security/Http/Firewall/AbstractAuthenticationListener.php
Строк: 540
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SymfonyComponentSecurityHttpFirewall;
use SymfonyComponentSecurityHttpSessionSessionAuthenticationStrategyInterface;
use SymfonyComponentSecurityHttpAuthenticationAuthenticationFailureHandlerInterface;
use SymfonyComponentSecurityHttpAuthenticationAuthenticationSuccessHandlerInterface;
use SymfonyComponentSecurityHttpRememberMeRememberMeServicesInterface;
use SymfonyComponentSecurityCoreSecurity;
use SymfonyComponentSecurityCoreAuthenticationAuthenticationManagerInterface;
use SymfonyComponentSecurityCoreAuthenticationTokenStorageTokenStorageInterface;
use SymfonyComponentSecurityCoreAuthenticationTokenUsernamePasswordToken;
use SymfonyComponentSecurityCoreExceptionAuthenticationException;
use SymfonyComponentSecurityCoreExceptionSessionUnavailableException;
use PsrLogLoggerInterface;
use SymfonyComponentHttpKernelEventGetResponseEvent;
use SymfonyComponentHttpFoundationRequest;
use SymfonyComponentHttpFoundationResponse;
use SymfonyComponentSecurityCoreAuthenticationTokenTokenInterface;
use SymfonyComponentEventDispatcherEventDispatcherInterface;
use SymfonyComponentSecurityHttpEventInteractiveLoginEvent;
use SymfonyComponentSecurityHttpSecurityEvents;
use SymfonyComponentSecurityHttpHttpUtils;
/**
* The AbstractAuthenticationListener is the preferred base class for all
* browser-/HTTP-based authentication requests.
*
* Subclasses likely have to implement the following:
* - an TokenInterface to hold authentication related data
* - an AuthenticationProvider to perform the actual authentication of the
* token, retrieve the UserInterface implementation from a database, and
* perform the specific account checks using the UserChecker
*
* By default, this listener only is active for a specific path, e.g.
* /login_check. If you want to change this behavior, you can overwrite the
* requiresAuthentication() method.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
abstract class AbstractAuthenticationListener implements ListenerInterface
{
protected $options;
protected $logger;
protected $authenticationManager;
protected $providerKey;
protected $httpUtils;
private $tokenStorage;
private $sessionStrategy;
private $dispatcher;
private $successHandler;
private $failureHandler;
private $rememberMeServices;
/**
* Constructor.
*
* @param TokenStorageInterface $tokenStorage A TokenStorageInterface instance
* @param AuthenticationManagerInterface $authenticationManager An AuthenticationManagerInterface instance
* @param SessionAuthenticationStrategyInterface $sessionStrategy
* @param HttpUtils $httpUtils An HttpUtilsInterface instance
* @param string $providerKey
* @param AuthenticationSuccessHandlerInterface $successHandler
* @param AuthenticationFailureHandlerInterface $failureHandler
* @param array $options An array of options for the processing of a
* successful, or failed authentication attempt
* @param LoggerInterface $logger A LoggerInterface instance
* @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance
*
* @throws InvalidArgumentException
*/
public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, $providerKey, AuthenticationSuccessHandlerInterface $successHandler, AuthenticationFailureHandlerInterface $failureHandler, array $options = array(), LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null)
{
if (empty($providerKey)) {
throw new InvalidArgumentException('$providerKey must not be empty.');
}
$this->tokenStorage = $tokenStorage;
$this->authenticationManager = $authenticationManager;
$this->sessionStrategy = $sessionStrategy;
$this->providerKey = $providerKey;
$this->successHandler = $successHandler;
$this->failureHandler = $failureHandler;
$this->options = array_merge(array(
'check_path' => '/login_check',
'login_path' => '/login',
'always_use_default_target_path' => false,
'default_target_path' => '/',
'target_path_parameter' => '_target_path',
'use_referer' => false,
'failure_path' => null,
'failure_forward' => false,
'require_previous_session' => true,
), $options);
$this->logger = $logger;
$this->dispatcher = $dispatcher;
$this->httpUtils = $httpUtils;
}
/**
* Sets the RememberMeServices implementation to use.
*
* @param RememberMeServicesInterface $rememberMeServices
*/
public function setRememberMeServices(RememberMeServicesInterface $rememberMeServices)
{
$this->rememberMeServices = $rememberMeServices;
}
/**
* Handles form based authentication.
*
* @param GetResponseEvent $event A GetResponseEvent instance
*
* @throws RuntimeException
* @throws SessionUnavailableException
*/
final public function handle(GetResponseEvent $event)
{
$request = $event->getRequest();
if (!$this->requiresAuthentication($request)) {
return;
}
if (!$request->hasSession()) {
throw new RuntimeException('This authentication method requires a session.');
}
try {
if ($this->options['require_previous_session'] && !$request->hasPreviousSession()) {
throw new SessionUnavailableException('Your session has timed out, or you have disabled cookies.');
}
if (null === $returnValue = $this->attemptAuthentication($request)) {
return;
}
if ($returnValue instanceof TokenInterface) {
$this->sessionStrategy->onAuthentication($request, $returnValue);
$response = $this->onSuccess($request, $returnValue);
} elseif ($returnValue instanceof Response) {
$response = $returnValue;
} else {
throw new RuntimeException('attemptAuthentication() must either return a Response, an implementation of TokenInterface, or null.');
}
} catch (AuthenticationException $e) {
$response = $this->onFailure($request, $e);
}
$event->setResponse($response);
}
/**
* Whether this request requires authentication.
*
* The default implementation only processes requests to a specific path,
* but a subclass could change this to only authenticate requests where a
* certain parameters is present.
*
* @param Request $request
*
* @return bool
*/
protected function requiresAuthentication(Request $request)
{
return $this->httpUtils->checkRequestPath($request, $this->options['check_path']);
}
/**
* Performs authentication.
*
* @param Request $request A Request instance
*
* @return TokenInterface|Response|null The authenticated token, null if full authentication is not possible, or a Response
*
* @throws AuthenticationException if the authentication fails
*/
abstract protected function attemptAuthentication(Request $request);
private function onFailure(Request $request, AuthenticationException $failed)
{
if (null !== $this->logger) {
$this->logger->info('Authentication request failed.', array('exception' => $failed));
}
$token = $this->tokenStorage->getToken();
if ($token instanceof UsernamePasswordToken && $this->providerKey === $token->getProviderKey()) {
$this->tokenStorage->setToken(null);
}
$response = $this->failureHandler->onAuthenticationFailure($request, $failed);
if (!$response instanceof Response) {
throw new RuntimeException('Authentication Failure Handler did not return a Response.');
}
return $response;
}
private function onSuccess(Request $request, TokenInterface $token)
{
if (null !== $this->logger) {
$this->logger->info('User has been authenticated successfully.', array('username' => $token->getUsername()));
}
$this->tokenStorage->setToken($token);
$session = $request->getSession();
$session->remove(Security::AUTHENTICATION_ERROR);
$session->remove(Security::LAST_USERNAME);
if (null !== $this->dispatcher) {
$loginEvent = new InteractiveLoginEvent($request, $token);
$this->dispatcher->dispatch(SecurityEvents::INTERACTIVE_LOGIN, $loginEvent);
}
$response = $this->successHandler->onAuthenticationSuccess($request, $token);
if (!$response instanceof Response) {
throw new RuntimeException('Authentication Success Handler did not return a Response.');
}
if (null !== $this->rememberMeServices) {
$this->rememberMeServices->loginSuccess($request, $response, $token);
}
return $response;
}
}