我正在开发一个 Symfony 2.2 项目,在该项目中,我使用“FOSUserBundle”来保证安全,效果很好。我也在使用“ewzrecaptchabundle”,它也可以正常工作。我的问题是我想在 FOSUserBundle 的登录页面中使用 recaptcha。我按照将验证码添加到 Symfony2 登录页面链接来覆盖防火墙,但仍然在制作新表单并传递 recaptcha 之后它没有检查 recaptcha 值。
我现在编辑了我的代码如下:
我的听众:
<?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 Webmuch\UserBundle\Listener;
use Symfony\Component\Security\Http\Firewall\UsernamePasswordFormAuthenticationListener as BaseListener;
use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface;
use Symfony\Component\HttpFoundation\Request;
use Psr\Log\LoggerInterface;
/**
* CanduUserLoginFormListener is the custom implementation of
* an authentication via a simple form composed of a username and a password.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class UserLoginFormListener 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' => 'ewz_recaptcha',
'intention' => 'authenticate',
'post_only' => true,
), $options), $logger, $dispatcher);
$this->csrfProvider = $csrfProvider;
}
/**
* {@inheritdoc}
*/
protected function requiresAuthentication(Request $request)
{
if ($this->options['post_only'] && !$request->isMethod('POST')) {
return false;
}
return parent::requiresAuthentication($request);
}
/**
* {@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.');
}
}
// 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));
}
}
而我在 parameters.yml 中将其设置为服务,如下所示
parameters:
database_driver: pdo_mysql
database_host: localhost
database_port: null
database_name: Project
database_user: root
database_password: root
mailer_transport: smtp
mailer_host: smtp.gmail.com
mailer_auth_mode: login
mailer_user: mymail@gmail.com
mailer_password: mymailpassword
locale: en
secret: ThisTokenIsNotSoSecretChangeIt
database_path: null
security.authentication.listener.form.class: Webmuch\UserBundle\Listener\UserLoginFormListener
然后在我创建了一个 UserFormType 如下:
<?php
/*
* This file is part of the FOSUserBundle package.
*
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Webmuch\UserBundle\Form;
class NewUserLoginFormType extends AbstractType
{
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,))
->add('recaptcha', 'ewz_recaptcha', array(
'attr' => array(
'options' => array(
'theme' => 'red'
)
),
'label' => "Verification",
'property_path' => false,
'constraints' => array(
new True()
),
'label' => "Enter the words in the box."))
->add('recaptcha_challenge_field', 'hidden', array('property_path' => false))
->add('recaptcha_response_field', 'hidden', array('property_path' => false));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Webmuch\UserBundle\Entity\User',
'intention' => 'authenticate',
));
}
public function getName()
{
return 'webmuch_user_newloginform';
}
}
在我的安全控制器中:
public function loginAction()
{
$form = $this->container->get('form.factory')->create(new NewUserLoginFormType());
$request = $this->container->get('request');
/* @var $request \Symfony\Component\HttpFoundation\Request */
$session = $request->getSession();
/* @var $session \Symfony\Component\HttpFoundation\Session */
// get the error if any (works with forward and redirect -- see below)
if ($request->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) {
$error = $request->attributes- >get(SecurityContext::AUTHENTICATION_ERROR);
} elseif (null !== $session && $session->has(SecurityContext::AUTHENTICATION_ERROR)) {
$error = $session->get(SecurityContext::AUTHENTICATION_ERROR);
$session->remove(SecurityContext::AUTHENTICATION_ERROR);
} else {
$error = '';
}
if ($error) {
// TODO: this is a potential security risk (see http://trac.symfony- project.org/ticket/9523)
//$error = $error->getMessage();
$session = $this->container->get('session');
$session->setFlash('error','Invalid Username or Password');
return new RedirectResponse($this->container->get('router')- >generate('fos_user_security_login'));
}
// last username entered by the user
$lastUsername = (null === $session) ? '' : $session- >get(SecurityContext::LAST_USERNAME);
$csrfToken = $this->container->get('form.csrf_provider')->generateCsrfToken('authenticate');
return $this->renderLogin(array( `enter code here`
'last_username' => $lastUsername,
'error' => $error,
'csrf_token' => $csrfToken,
'form' => $form->createView(),
));
}
最后是 login.html.twig:
{% extends "::base1.html.twig" %}
{% block userProfile %}
{% if error %}
<div>{{ error|trans({}, 'FOSUserBundle') }}</div>
{% endif %}
<h2 class="gradWellHead">Login here</h2>
<div class="row-fluid marginBottom10">
<div class="span6 well">
<form class="form-horizontal" action="{{ path("fos_user_security_check") }}" method="post">
<input type="hidden" name="_csrf_token" value="{{ csrf_token }}" />
<div class="control-group">
<label class="control-label" for="username">{{ 'form.username'|trans({}, 'FOSUserBundle') }}</label>
<div class="controls">
<input type="text" id="username" name="_username" value="{{ last_username }}" required="required" placeholder="Username"/>
</div>
</div>
<div class="control-group">
<label class="control-label" for="password">{{ 'form.password'|trans({}, 'FOSUserBundle') }}</label>
<div class="controls">
<input type="password" id="password" name="_password" required="required" placeholder="Password"/>
</div>
</div>
<div class="control-group">
<label class="control-label" for="recaptcha">Recaptcha</label>
<div class="controls">
{% form_theme form 'EWZRecaptchaBundle:Form:ewz_recaptcha_widget.html.twig' %}
{{ form_widget(form.recaptcha, { 'attr': {'options' : {'theme' : 'clean',},} }) }}
</div>
</div>
<div class="control-group">
<div class="controls">
<label class="checkbox">
<input type="checkbox" id="remember_me" name="_remember_me" value="on" />Remember me?
</label>
<input class="btn" type="submit" id="_submit" name="_submit" value="{{ 'form.submit'|trans({}, 'FOSUserBundle') }}" />
<a href="{{ path('fos_user_resetting_request') }}">Forget Password ?</a>
{{ facebook_login_button({'autologoutlink': true}) }}
</div>
</div>
</form>
</div>
<div class="span6 well">
<img src="{{asset('img/candu_manifesto_starburst.jpg')}}">
</div>
</div>
{% endblock %}
如果有任何想法,请帮助我。
提前致谢 !