Прежде всего, я использую Symfony 2.1.7. И FOSUserBundle для пользовательских настроек. Я уже переопределил шаблон fos_user-login с именем пользователя и паролем. Но я хочу добавить капчу для входа в систему. Я видел GregwarCaptchaBundle, и согласно документу, в FormType должно быть добавлено новое поле. И у меня возникает вопрос: где находится тип формы входа в систему symfony или FOSUserBundle, в который я могу добавить это новое поле или переопределить его? Существует ChangePasswordFormType, ProfileFormType... и т. д., но нет LoginFOrmType. Может быть, это так очевидно, но я не понял сути. Любая помощь приветствуется, пожалуйста.
ВОПРОС КАК-ТО РЕДАКТИРОВАНО С РЕШЕНИЕМ
Возьмите посмотрите комментарии ниже, Патт помог мне.
Я создал новый тип формы с полями _username, _password и captcha. Когда имя пользователя и пароль начинаются с подчеркивания, этого достаточно для маршрутизации «login_check» и аутентификации Symfony. Однако Symfony использует прослушиватель для процесса входа в систему.
Это класс UsernamePasswordFormAuthenticationListener. Хотя я добавил поле капчи в тип формы, оно всегда игнорируется во время процесса входа в систему. (Оно отображается на странице, но поле никогда не проверяется, оно просто игнорируется.)
Код: Выделить всё
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('_username', 'email', array('label' => 'form.username', 'translation_domain' => 'FOSUserBundle')) // TODO: user can login with email by inhibit the user to enter username
->add('_password', 'password', array(
'label' => 'form.current_password',
'translation_domain' => 'FOSUserBundle',
'mapped' => false,
'constraints' => new UserPassword()))
->add('captcha', 'captcha');
}
Код: Выделить всё
public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, $providerKey, AuthenticationSuccessHandlerInterface $successHandler, AuthenticationFailureHandlerInterface $failureHandler, array $options = array(), LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, CsrfProviderInterface $csrfProvider = null)
{
parent::__construct($securityContext, $authenticationManager, $sessionStrategy, $httpUtils, $providerKey, $successHandler, $failureHandler, array_merge(array(
'username_parameter' => '_username',
'password_parameter' => '_password',
'csrf_parameter' => '_csrf_token',
'captcha' => 'captcha',
'intention' => 'authenticate',
'post_only' => true,
), $options), $logger, $dispatcher);
$this->csrfProvider = $csrfProvider;
}
Код: Выделить всё
protected function attemptAuthentication(Request $request)
{
if ($this->options['post_only'] && 'post' !== strtolower($request->getMethod())) {
if (null !== $this->logger) {
$this->logger->debug(sprintf('Authentication method not supported: %s.', $request->getMethod()));
}
return null;
}
if (null !== $this->csrfProvider) {
$csrfToken = $request->get($this->options['csrf_parameter'], null, true);
if (false === $this->csrfProvider->isCsrfTokenValid($this->options['intention'], $csrfToken)) {
throw new InvalidCsrfTokenException('Invalid CSRF token.');
}
}
// check here the captcha value
$userCaptcha = $request->get($this->options['captcha'], null, true);
$dummy = $request->getSession()->get('gcb_captcha');
$sessionCaptcha = $dummy['phrase'];
// if captcha is not correct, throw exception
if ($userCaptcha !== $sessionCaptcha) {
throw new BadCredentialsException('Captcha is invalid');
}
$username = trim($request->get($this->options['username_parameter'], null, true));
$password = $request->get($this->options['password_parameter'], null, true);
$request->getSession()->set(SecurityContextInterface::LAST_USERNAME, $username);
return $this->authenticationManager->authenticate(new UsernamePasswordToken($username, $password, $this->providerKey));
}
Играть с кодом Symfony — не лучший способ, я знаю. Если я найду какой-нибудь способ переопределить и вызвать свою собственную функцию, я опубликую его.
ЕЩЁ ПОЛЕЗНЫЙ ОТВЕТ
Я нашел еще один ответ, который может быть полезен
[link]Существует ли какое-либо событие «предварительного входа» или подобное?
Следуя этому решению, Я просто переопределил класс UsernamePasswordFormAuthenticationListener и переопределил параметр прослушивателя безопасности security.authentication.listener.form.class. Вот код:
Код: Выделить всё
namespace TCAT\StaffBundle\Listener;
use Symfony\Component\Security\Http\Firewall\UsernamePasswordFormAuthenticationListener as BaseListener; use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Log\LoggerInterface; use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface; use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface; use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface; use Symfony\Component\Security\Http\HttpUtils; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException; use Symfony\Component\Security\Core\SecurityContextInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Security\Core\Exception\BadCredentialsException;
class StaffLoginFormListener extends BaseListener
{
private $csrfProvider;
/**
* {@inheritdoc}
*/
public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, $providerKey, AuthenticationSuccessHandlerInterface $successHandler, AuthenticationFailureHandlerInterface $failureHandler, array $options
= array(), LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, CsrfProviderInterface $csrfProvider = null)
{
parent::__construct($securityContext, $authenticationManager, $sessionStrategy, $httpUtils, $providerKey, $successHandler, $failureHandler, array_merge(array(
'username_parameter' => '_username',
'password_parameter' => '_password',
'csrf_parameter' => '_csrf_token',
'captcha' => 'captcha',
'intention' => 'authenticate',
'post_only' => true,
), $options), $logger, $dispatcher);
$this->csrfProvider = $csrfProvider;
}
/**
* {@inheritdoc}
*/
protected function attemptAuthentication(Request $request)
{
if ($this->options['post_only'] && 'post' !== strtolower($request->getMethod())) {
if (null !== $this->logger) {
$this->logger->debug(sprintf('Authentication method not supported: %s.', $request->getMethod()));
}
return null;
}
if (null !== $this->csrfProvider) {
$csrfToken = $request->get($this->options['csrf_parameter'], null, true);
if (false === $this->csrfProvider->isCsrfTokenValid($this->options['intention'], $csrfToken)) {
throw new InvalidCsrfTokenException('Invalid CSRF token.');
}
}
// throw new BadCredentialsException('Bad credentials');
$userCaptcha = $request->get($this->options['captcha'], null, true);
$dummy = $request->getSession()->get('gcb_captcha');
$sessionCaptcha = $dummy['phrase'];
if ($userCaptcha !== $sessionCaptcha) {
throw new BadCredentialsException('Captcha is invalid');
}
$username = trim($request->get($this->options['username_parameter'], null, true));
$password = $request->get($this->options['password_parameter'], null, true);
$request->getSession()->set(SecurityContextInterface::LAST_USERNAME, $username);
return $this->authenticationManager->authenticate(new UsernamePasswordToken($username, $password, $this->providerKey));
}
}
Кстати, я могу проверить значение своей капчи. Надеюсь, у вас все получится.
Подробнее здесь: https://stackoverflow.com/questions/147 ... login-page
Мобильная версия