Symfony - 即使名称更改,用户也会被注销

时间:2016-04-25 08:10:56

标签: php symfony

我面临一个非常奇怪的情况,一旦我更改名称,ROLE_ADMIN用户就会被注销,即使我没有更改任何内容并按下保存按钮,它也会被注销。如果我将用户的角色直接在数据库中更改为ROLE_USER,则相同的代码可以正常工作,用户也不会注销。

以下是负责配置文件更新的控制器

    /**
     * @Route("/profile", name="profile")
     */
    public function profileAction(Request $request)
    {

        $em = $this->getDoctrine()->getManager();

        $userInfo = $this->getUser();

        //create the form object
        $profileForm = $this->createForm(UserType::class, $userInfo);
        $profileForm->handleRequest($request);


        //check data validity
        if($profileForm->isValid()){
            $em->persist($userInfo);
            $em->flush();
            $this->get('session')->getFlashBag()->add(
                'success',
                'Your profile information has been updated'
            );

            return $this->render('AppBundle:admin/user:admin-edit.html.twig',array(
                'edit_form' => $profileForm->createView()
            ));
        }


        // render registration form
        return $this->render('AppBundle:admin/user:admin-edit.html.twig',array(
            'edit_form' => $profileForm->createView()
        ));
    }

}

这是我的security.yml

security:
    encoders:
        # Our user class and the algorithm we'll use to encode passwords
        # http://symfony.com/doc/current/book/security.html#encoding-the-user-s-password
        AppBundle\Entity\User: bcrypt

    providers:
        # Simple example of loading users via Doctrine
        # To load users from somewhere else: http://symfony.com/doc/current/cookbook/security/custom_provider.html
        database_users:
            entity: { class: AppBundle:User, property: username }

    firewalls:
        # disables authentication for assets and the profiler, adapt it according to your needs
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false

        main:
            http_basic: ~
            anonymous: ~
            logout: ~
            guard:
                authenticators:
                    - app.form_login_authenticator
                    - app.facebook_authenticator
                # by default, use the start() function from FormLoginAuthenticator
                entry_point: app.form_login_authenticator
    access_control:
        - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/, roles: ROLE_ADMIN }
        - { path: ^/user, roles: ROLE_USER }

更新1

这是我的UserType

namespace AppBundle\Form;

use AppBundle\Form\EventListener\AddDepartmentDegreeCourseFieldSubscriber;
use AppBundle\Form\EventListener\AddProfileFieldSubscriber;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;


class UserType extends AbstractType
{

    private $addDepartmentDegreeCourseFieldSubscriber;
    private $addProfileFieldSubscriver;

    function __construct(AddDepartmentDegreeCourseFieldSubscriber $subscriber, AddProfileFieldSubscriber $fields)
    {
        $this->addDepartmentDegreeCourseFieldSubscriber = $subscriber;
        $this->addProfileFieldSubscriver = $fields;
    }

    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->addEventSubscriber($this->addProfileFieldSubscriver);
        $builder->addEventSubscriber($this->addDepartmentDegreeCourseFieldSubscriber);
    }

    /**
     * @param OptionsResolver $resolver
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'AppBundle\Entity\User'
        ));
    }
}

这是我的AddProfileFieldSubscriber

namespace AppBundle\Form\EventListener;


use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationChecker;
use Symfony\Component\Validator\Constraints\NotBlank;


class AddProfileFieldSubscriber implements EventSubscriberInterface
{
    protected $authorizationChecker;


    function __construct(AuthorizationChecker $authorizationChecker)
    {
        $this->authorizationChecker = $authorizationChecker;
    }

    public static function getSubscribedEvents()
    {
        // Tells the dispatcher that you want to listen on the form.pre_set_data
        // event and that the preSetData method should be called.
        return array(FormEvents::PRE_SET_DATA => 'preSetData');
    }

    public function preSetData(FormEvent $event)
    {


        $user = $event->getData();
        $form = $event->getForm();




        if($user){
            $form->add('firstName', TextType::class);
            $form->add('lastName', TextType::class);
            $form->add('password', PasswordType::class, array(
                'mapped' => false
            ));
            $form->add('profileImage', FileType::class, array(
                'data_class' => null
            ));
            if (in_array("ROLE_USER", $user->getRoles())) {
                $form->add('contactNumber', TextType::class);

                $form->add('gender', ChoiceType::class, array(
                    'choices' => array(
                        'Male' => 'm',
                        'Female' => 'f'
                    ),
                    'placeholder' => 'provide_gender'
                ));
                $form->add('college', EntityType::class, array(
                        'placeholder' => 'provide_college',
                        'class' => 'AppBundle\Entity\College')
                );
                $form->add('interest', EntityType::class, array(
                        'class' => 'AppBundle\Entity\Interest',
                        'multiple' => true,
                        'expanded' => false,
                        'by_reference' => false,
                    )
                );

            }

            if($this->authorizationChecker->isGranted('ROLE_ADMIN')  ) {

                $form->add('isActive', ChoiceType::class, array(
                    'choices' => array(
                        'account_active' => '1',
                        'account_inactive' => '0'
                    ),
                    'placeholder' => 'provide_status'
                ));
            }

        }
        //if the selected user has role_user only then display the following fields in edit profile view
       else {
            $form->add('username', EmailType::class);
            $form->add('password', PasswordType::class, array(
                'constraints' => array(new NotBlank(array(
                        'message' => 'user.password.not_blank'
                    )
                ),),
            ));
        }
    }
}

这是AddDepartmentDegreeCourseFieldSubscriber

namespace AppBundle\Form\EventListener;


use AppBundle\Entity\Degree;
use AppBundle\Entity\Department;
use Doctrine\ORM\EntityManager;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormInterface;

class AddDepartmentDegreeCourseFieldSubscriber implements EventSubscriberInterface
{

    protected $em;


    function __construct(EntityManager $em)
    {
        $this->em = $em;
    }

    public static function getSubscribedEvents()
    {
        // Tells the dispatcher that you want to listen on the form.pre_set_data
        // event and that the preSetData method should be called.
        return array(
            FormEvents::PRE_SET_DATA => 'onPreSetData',
            FormEvents::PRE_SUBMIT => 'onPreSubmit'
        );
    }
    protected function addElements(FormInterface $form, Department $departments = null, Degree $degree = null)
    {

        // Add the department element
        $form->add('department', EntityType::class, array(
                'data' => $departments,
                'placeholder' => 'provide_department',
                'class' => 'AppBundle\Entity\Department')
        );
        // Degree are empty, unless we actually supplied a department
        $degree = array();
        if ($departments) {
            // Fetch the courses from specified degree
            $repo = $this->em->getRepository('AppBundle:Degree');
            $degree = $repo->findByDepartment($departments, array('name' => 'asc'));
        }


        // Add the province element
        $form->add('degree', EntityType::class, array(
                'placeholder' => 'provide_degree',
                'class' => 'AppBundle\Entity\Degree',
                'choices' => $degree)
        );
        // Cities are empty, unless we actually supplied a province
        $courses = array();
        if ($degree) {
            // Fetch the cities from specified province
            $repo = $this->em->getRepository('AppBundle:Course');
            $courses = $repo->findByDegree($degree, array('name' => 'asc'));
        }
        // Add the Course element
        $form->add('course', EntityType::class, array(
            'class' => 'AppBundle\Entity\Course',
            'choices' => $courses,
        ));

    }
    function onPreSubmit(FormEvent $event) {

        $form = $event->getForm();
        $data = $event->getData();
        if (isset($data['degree'])) {

            // Note that the data is not yet hydrated into the entity.
            $degree = $this->em->getRepository('AppBundle:Degree')->find($data['degree']);
            $department = $this->em->getRepository('AppBundle:Department')->find($data['department']);
            $this->addElements($form, $department, $degree);
        }
    }
    function onPreSetData(FormEvent $event) {
        //echo "before submit";die;
        $user = $event->getData();
        $form = $event->getForm();



        if($user){
            //if the selected user has role_user only then display the following fields in edit profile view
            if (in_array("ROLE_USER", $user->getRoles())) {
                $degree = ( !empty($user) && !empty($user->getCourse())) ? $user->getCourse()->getDegree() : null;
                $departments = ( !empty($user) && !empty($user->getCourse())) ? $user->getCourse()->getDegree()->getDepartment() : null;
                $this->addElements($form, $departments, $degree);
            }

        }

    }
}

在这一点上,我真的无能为力导致这种情况,我真的很感激这里的任何帮助......

1 个答案:

答案 0 :(得分:0)

你所提供的内容有很多不妥之处。其中的元素可能会导致您所看到的行为。

安全规则

在security.yml中,你有这一行:

- { path: ^/admin/, roles: ROLE_ADMIN }

这意味着如果没有ROLE_ADMIN的任何使用,如果他们访问此模式,将会被回击到登录屏幕。

同时,在您的控制器中,您始终将用户引导回基于管理员的路线:

/*
 * @Route("admin/profile", name="admin_profile")
 */

这意味着,无论他们做什么,他们总是被发送到管理模式。这意味着如果他们改变了自己的角色,他们就会被防火墙踢掉。

<强>控制器

在您的控制器中,您将用户实体绑定到formtype,很好。但是你编码的方式意味着你不明白它是如何工作的。

当您调用handleRequest时,Symfony会从表单中提取数据(如果已提交),并将其与您传递给它的实体“合并”。这意味着您不必在对象上调用任何setter,因为已经为您完成了所有这些。

用户实体

User实体应拥有的内容是PlainPassword字段和Password字段。表单只映射到PlainPassword字段..然后,在控制器中,获取PlainPassword值,对其进行编码,将其设置为实体的Password字段,并确保清除PlainPassword值(你不想存储它)。如果您在自定义用户实体上实现了UserInterface(您应该拥有),则应该有一个名为eraseCredentials的方法,这就是它的用途。 Here is an example

这样做的结果是,当您检查某些内容时,例如新密码时,您只需执行此操作:

if($profileForm->isValid()){
        // $userInfo is populated by handleRequest
        if ($userInfo->getPlainPassword()) {
          // do your encoding/erasing credentials here
        }
        // ...

}

我还建议你写一个合适的UserManager课来处理大部分事情。它集中了事物并使调试更容易。 Heres an example

如果您使用的内容如我在示例中所写的那样,这也意味着当您想要更新用户信息时,您只需要拨打$userManager->updateUser($user)并完成所有驴工作对你而言。