Symfony 2 SecurityBundle通过表单登录进行身份验证会引发BadCredentialsException

时间:2012-04-09 10:38:50

标签: symfony

这是我的实体:

<?php

namespace Trade\TradeBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\UserInterface;

/**
 * @ORM\Entity(repositoryClass = "Trade\TradeBundle\Repository\UserRepository")
 * @ORM\Table(name = "user")
 * @UniqueEntity(fields = "username", message = "The username already exists.")
 */
class User implements UserInterface
{
/**
 * @ORM\id
 * @ORM\Column(name = "id", type = "integer")
 * @ORM\GeneratedValue(strategy = "AUTO")
 */
protected $id;

/**
 * @ORM\Column(name = "username", type = "string", length = 20, unique = true)
 *
 * @Assert\NotBlank(
 *      message = "The username cannot be blank."
 * )
 */
protected $username;

/**
 * @ORM\Column(name = "password", type = "string", length = 40)
 *
 * @Assert\NotBlank(
 *      message = "The password cannot be blank."
 * )
 * @Assert\MinLength(
 *      limit = 8,
 *      message = "The password '{{ value }}' is too short. It should have {{ limit }} characters or more."
 * )
 * @Assert\MaxLength(
 *      limit = 16,
 *      message = "The password '{{ value }}' is too long. It should have {{ limit }} characters or less."
 * )
 */
protected $password;

/**
 * @ORM\Column(type = "string", length = 40)
 */
protected $salt;

public function __construct()
{
    $this->salt = md5(uniqid(null, true));
}

/**
 * Get id
 *
 * @return integer
 */
public function getId()
{
    return $this->id;
}

/**
 * Set username
 *
 * @param string $username
 */
public function setUsername($username)
{
    $this->username = $username;
}

/**
 * Get username
 *
 * @return string
 */
public function getUsername()
{
    return $this->username;
}

/**
 * Set password
 *
 * @param string $password
 */
public function setPassword($password)
{
    $this->password = $password;
}

/**
 * Get password
 *
 * @return string
 */
public function getPassword()
{
    return $this->password;
}

public function getSalt()
{
    return $this->salt;
}

public function getRoles()
{
    return array('ROLE_USER');
}

public function eraseCredentials()
{
}

public function equals(UserInterface $user)
{
    return $this->username === $user->getUsername();
}
}

这是我的存储库:

<?php

namespace Trade\TradeBundle\Repository;

use Doctrine\ORM\EntityRepository;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\UserInterface;

/**
 * UserRepository
 *
 * This class was generated by the Doctrine ORM. Add your own custom
 * repository methods below.
 */
class UserRepository extends EntityRepository implements UserProviderInterface
{
public function loadUserByUsername($username)
{
    return $this->getEntityManager()->findOneByUsername($username);
}

public function refreshUser(UserInterface $user)
{
    return $this->loadUserByUsername($user->getUsername());
}

public function supportsClass($class)
{
    return $class === 'Trade\TradeBundle\Entity\User';
}
}

这是security.yml:

security:
    encoders:
        Trade\TradeBundle\Entity\User:
            algorithm: sha1
            encode_as_base64: false
            iterations: 1

role_hierarchy:
    ROLE_ADMIN:       ROLE_USER
    ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]

providers:
    main:
        entity: { class: TradeTradeBundle:User, property: username }

firewalls:
    dev:
        pattern:  ^/(_(profiler|wdt)|css|images|js)/
        security: false

    secured_area:
        pattern:    ^/
        form_login:
            check_path: /account/signin_check
            login_path: /account/signin
        logout:
            path:   /account/signout
            target: /
        anonymous: ~

access_control:
    - { path: ^/account/signin, roles: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/account, roles: ROLE_USER }
    - { path: ^/admin, roles: ROLE_ADMIN }

这是我的控制器操作:

/**
 * @Route("/account/signin", name = "account_signin")
 * @Route("/account/signin_check", name = "account_signin_check")
 * @Method({"GET", "POST"})
 */
public function signInAction()
{
    $request = $this->getRequest();
    $session = $request->getSession();

    $user = new \Trade\TradeBundle\Entity\User();
    $form = $this->createForm(new \Trade\TradeBundle\Form\Type\UserType(), $user);

    if ($request->attributes->has(SecurityContext::AUTHENTICATION_ERROR))
    {
        $error = $request->attributes->get(SecurityContext::AUTHENTICATION_ERROR);
    }
    else
    {
        $error = $session->get(SecurityContext::AUTHENTICATION_ERROR);
        $session->remove(SecurityContext::AUTHENTICATION_ERROR);
    }

    return $this->render('TradeTradeBundle:Account:signin.html.twig', array('form' => $form->createView(), 'error' => $error));
}

这是我的表格类:

<?php

namespace Trade\TradeBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;


class UserType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
    $builder->add('username', 'text', array('required' => false));
    $builder->add('password', 'password', array('required' => false));
}

public function getName()
{
    return 'user';
}

public function getDefaultOptions(array $options)
{
    return array(
        'data_class' => 'Trade\TradeBundle\Entity\User'
    );
}
}

这是我的表格:

{% if error %}
    <div class="alert alert-error">{{ error }}</div>
{% endif %}
<form action="{{ path('account_signin_check') }}" method="post" class="form-inline" novalidate>
    <table class="table">
        <tr>
            <td style="width: 120px;">{{ form_label(form.username) }} <span class="required">*</span></td>
            <td style="width: 220px;">{{ form_widget(form.username, { 'attr': {'placeholder': 'Username'} }) }}</td>
            <td class="form-error" id="email_error">{{ form_errors(form.username) }}</td>
        </tr>
        <tr>
            <td>{{ form_label(form.password) }} <span class="required">*</span></td>
            <td>{{ form_widget(form.password, { 'attr': {'placeholder': 'Password'} }) }}</td>
            <td class="form-error" id="password_error">{{ form_errors(form.password) }}</td>
        </tr>
        <tr>
            <td colspan="2" style="text-align: right;">
                {{ form_rest(form) }}
                <input type="reset" value="Cancel" class="btn" />
                <input type="submit" value="Sign in" class="btn btn-danger" />
            </td>
        <td>&nbsp;</td>
        </tr>
    </table>
</form>

当我尝试登录时,会显示BadCredentialsException。我不知道为什么。我想我根据Symfony的文档做了一切正确的请帮助。

2 个答案:

答案 0 :(得分:1)

尝试使用像_username和_password这样的输入名称,或者在security.yml中尝试将其放在secured_area下

username_parameter: username
password_parameter: password

也许它可以帮到你!

答案 1 :(得分:0)

这已经很久了,但我希望我的回答可以帮助别人。

我试图将表单组件的使用与安全组件的使用完全相同。

不幸的是,这是不可能的,因为安全组件在调用login_check函数时依赖于输入形式“名称”参数(名称的html定义) _username _password 。不能使用表单组件定义(无法定义输入元素的此可选参数)。

使用

为元素命名时
  

- &gt;添加( 'MY_NAME',...

实际上这是被通知的元素的id参数,而不是“name”参数。 buidlForm函数给出的“name”将是“somename [my_name]”(其中somename是Form类的getName()函数返回的值),根据安全组件的要求,它不能是“_username”。

也许一个解决方法是在安全定义文件中将username_parameter定义为“somename [my_name]”,以便组件查找它,但我甚至没有尝试过这个,因为这是一个非常讨厌的黑客。

编辑: 实际上我用于另一个问题的另一个解决方法是在Form类的getName()函数中返回一个空字符串,因此我们得到:“my_name”作为输入元素属性而不是“somename [my_name]”。 所以也许我们可以尝试将“_username”和“_password”值赋予name属性(尚未尝试过)。