如何在自定义用户提供者上获取实体用户而不是Webserviceuser

问题描述

经过一些努力,我能够在Symfony中实现自定义用户提供程序,但是,它无法按预期运行,并且我很难发现自己去了哪里。 我遵循此Guide 但是我有一些我无法解决的问题。

1-为什么我必须同时使用Entity和WebSErviceuser编码?

AppBundle\Entity\User: bcrypt
        AppBundle\Security\User\WebserviceUser:
            algorithm: bcrypt
            cost: 12

2-为什么Symfony不会抛出任何错误?

3-还有一个主要问题:如何访问用户ID(我在BlogController上需要用户ID)

3-并且当尝试从Controller访问$this->getUser()时,我得到的是WebserviceUser

4-和salt.i将salt设置为随机字符串..所有这些最佳做法是吗?

我所有的代码:

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
        AppBundle\Security\User\WebserviceUser:
            algorithm: bcrypt
            cost: 12
    providers:
        # in this example,users are stored via Doctrine in the database
        # To see the users at src/AppBundle/DataFixtures/ORM/LoadFixtures.php
        # To load users from somewhere else: http://symfony.com/doc/current/cookbook/security/custom_provider.html
        webservice:
            #id: AppBundle\Security\User\WebserviceUserProvider
            id: app.webservice_user_provider
        #database_users:
            #entity: { class: AppBundle:User,property: username }

    # http://symfony.com/doc/current/book/security.html#firewalls-authentication
    firewalls:
        secured_area:
            # this firewall applies to all URLs
            pattern: ^/

            # but the firewall does not require login on every page
            # denying access is done in access_control or in your controllers
            anonymous: true

            # This allows the user to login by submitting a username and password
            # Reference: http://symfony.com/doc/current/cookbook/security/form_login_setup.html
            form_login:
                # The route name that the login form submits to
                check_path: security_login
                # The name of the route where the login form lives
                # When the user tries to access a protected page,they are redirected here
                login_path: security_login
                # Secure the login form against CSRF
                # Reference: http://symfony.com/doc/current/cookbook/security/csrf_in_login_form.html
                csrf_token_generator: security.csrf.token_manager
                # The page users are redirect to when there is no previous page stored in the
                # session (for example when the users access directly to the login page).
                default_target_path: blog_index

            logout:
                # The route name the user can go to in order to logout
                path: security_logout
                # The name of the route to redirect to after logging out
                target: homepage

    access_control:
        # this is a catch-all for the admin area
        # additional security lives in the controllers
        - { path: '^/(%app_locales%)/admin',roles: ROLE_ADMIN }

services.yml

services:
    app.webservice_user_provider:
        class: AppBundle\Security\User\WebserviceUserProvider
        autowire: true
    # First we define some basic services to make these utilities available in
    # the entire application
    slugger:
        class: AppBundle\Utils\Slugger

    markdown:
        class: AppBundle\Utils\Markdown

    # These are the Twig extensions that create new filters and functions for
    # using them in the templates
    app.twig.app_extension:
        public:    false
        class:     AppBundle\Twig\AppExtension
        arguments: ['@markdown','%app_locales%']
        tags:
            - { name: twig.extension }

    app.twig.intl_extension:
        public: false
        class:  Twig_Extensions_Extension_Intl
        tags:
            - { name: twig.extension }

    # Defining a form type as a service is only required when the form type
    # needs to use some other services,such as the entity manager.
    # See http://symfony.com/doc/current/best_practices/forms.html
    app.form.type.tagsinput:
        class: AppBundle\Form\Type\TagsInputType
        arguments: ['@doctrine.orm.entity_manager']
        tags:
            - { name: form.type }

    # Event Listeners are classes that listen to one or more specific events.
    # Those events are defined in the tags added to the service definition.
    # See http://symfony.com/doc/current/event_dispatcher.html#creating-an-event-listener
    app.redirect_to_preferred_locale_listener:
        class: AppBundle\EventListener\RedirectToPreferredLocaleListener
        arguments: ['@router','%app_locales%','%locale%']
        tags:
            - { name: kernel.event_listener,event: kernel.request,method: onKernelRequest }

    app.comment_notification:
        class: AppBundle\EventListener\CommentNotificationListener
        arguments: ['@mailer','@router','@translator','%app.notifications.email_sender%']
        # The "method" attribute of this tag is optional and defaults to "on + camelCasedEventName"
        # If the event is "comment.created" the method executed by default is "onCommentCreated()".
        tags:
            - { name: kernel.event_listener,event: comment.created,method: onCommentCreated }

    # Event subscribers are similar to event listeners but they don't need service tags.
    # Instead,the PHP class of the event subscriber includes a method that returns
    # the list of events listened by that class.
    # See http://symfony.com/doc/current/event_dispatcher.html#creating-an-event-subscriber
    app.requirements_subscriber:
        class: AppBundle\EventListener\CheckRequirementsSubscriber
        arguments: ['@doctrine.orm.entity_manager']
        tags:
            - { name: kernel.event_subscriber }

    # To inject the voter into the security layer,you must declare it as a service and tag it with security.voter.
    # See http://symfony.com/doc/current/security/voters.html#configuring-the-voter
    app.post_voter:
        class: AppBundle\Security\PostVoter
        public: false
        tags:
            - { name: security.voter }

    # Uncomment the following lines to define a service for the Post Doctrine repository.
    # It's not mandatory to create these services,but if you use repositories a lot,# these services simplify your code:
    #
    # app.post_repository:
    #     class:     Doctrine\ORM\EntityRepository
    #     factory:   ['@doctrine.orm.entity_manager',getRepository]
    #     arguments: [AppBundle\Entity\Post]
    #
    # // traditional code inside a controller
    # $entityManager = $this->getDoctrine()->getManager();
    # $posts = $entityManager->getRepository('AppBundle:Post')->findAll();
    #
    # // same code using repository services
    # $posts = $this->get('app.post_repository')->findAll();

WebserviceUserProvider.php

<?php 
// src/AppBundle/Security/User/WebserviceUserProvider.php
namespace AppBundle\Security\User;

use AppBundle\Entity\User;
use AppBundle\Security\User\WebserviceUser;
//use Doctrine\DBAL\Connection;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;

class WebserviceUserProvider implements UserProviderInterface
{
    private $em;
    
    public function __construct(EntityManager $entityManager)
    {
        $this->em = $entityManager;
    }
    
    public function loadUserByUsername($username)
    {
        $userData = $this->em->getRepository(User::class)->findOneBy(array('username' => $username));
        $row = $userData;
        if (!$row->getUsername())
        {
            $exception = new UsernameNotFoundException(sprintf('Username "%s" not found in the database.',$row->getUsername()));
            $exception->setUsername($username);
            throw $exception;
        }
        else
        {
            $salt = '54hg5g4hfjh4g5sdgf45gd4h84gjhdf54gf4g2f2gfdhggfdg';
            return new WebserviceUser($row->getUsername(),$row->getPassword(),$salt,$row->getRoles());
        }
        
        
    }
    
    public function refreshUser(UserInterface $user)
    {
        if (!$user instanceof WebserviceUser) {
            throw new UnsupportedUserException(
                sprintf('Instances of "%s" are not supported.',get_class($user))
                );
        }
        
        return $this->loadUserByUsername($user->getUsername());
    }
    
    public function supportsClass($class)
    {
        return WebserviceUser::class === $class;
    }
}
?>

WebserviceUser.php

<?php 
// src/AppBundle/Security/User/WebserviceUser.php

namespace AppBundle\Security\User;

use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\EquatableInterface;

class WebserviceUser implements UserInterface,EquatableInterface
{
    private $username;
    private $password;
    private $salt;
    private $roles;
    //
    public function __construct(string $username,string $password,string $salt,array $roles)
    {
        if (empty($username))
        {
            throw new \InvalidArgumentException('No username provided.');
        }
        $this->username = $username;
        $this->password = $password;
        $this->salt = $salt;
        $this->roles = $roles;
    }
    
    public function getRoles()
    {
        return $this->roles;
    }
    
    public function getPassword()
    {
        return $this->password;
    }
    
    public function getSalt()
    {
        return $this->salt;
    }
    
    public function getUsername()
    {
        return $this->username;
    }
    
    public function eraseCredentials()
    {
    }
    
    public function isEqualTo(UserInterface $user)
    {
        if (!$user instanceof WebserviceUser) {
            return false;
        }
        
        if ($this->password !== $user->getPassword()) {
            return false;
        }
        
        if ($this->salt !== $user->getSalt()) {
            return false;
        }
        
        if ($this->username !== $user->getUsername()) {
            return false;
        }
        
        return true;
    }
}
?>

BlogController.php

<?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 AppBundle\Controller\Admin;

use AppBundle\Entity\Post;
use AppBundle\Form\PostType;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\User\UserInterface;

/**
 * Controller used to manage blog contents in the backend.
 *
 * Please note that the application backend is developed manually for learning
 * purposes. However,in your real Symfony application you should use any of the
 * existing bundles that let you generate ready-to-use backends without effort.
 *
 * See http://knpbundles.com/keyword/admin
 *
 * @Route("/admin/post")
 * @Security("has_role('ROLE_ADMIN')")
 *
 * @author Ryan Weaver <weaverryan@gmail.com>
 * @author Javier Eguiluz <javier.eguiluz@gmail.com>
 */
class BlogController extends Controller
{
    /**
     * Lists all Post entities.
     *
     * This controller responds to two different routes with the same URL:
     *   * 'admin_post_index' is the route with a name that follows the same
     *     structure as the rest of the controllers of this class.
     *   * 'admin_index' is a nice shortcut to the backend homepage. This allows
     *     to create simpler links in the templates. Moreover,in the future we
     *     could move this annotation to any other controller while maintaining
     *     the route name and therefore,without breaking any existing link.
     *
     * @Route("/",name="admin_index")
     * @Route("/",name="admin_post_index")
     * @Method("GET")
     */
    public function indexAction()
    {
        dump($this->getUser());
        $entityManager = $this->getDoctrine()->getManager();
        $posts = $entityManager->getRepository(Post::class)->findBy(['author' => $this->getUser()],['publishedAt' => 'DESC']);

        return $this->render('admin/blog/index.html.twig',['posts' => $posts]);
    }

    /**
     * Creates a new Post entity.
     *
     * @Route("/new",name="admin_post_new")
     * @Method({"GET","POST"})
     *
     * NOTE: the Method annotation is optional,but it's a recommended practice
     * to constraint the HTTP methods each controller responds to (by default
     * it responds to all methods).
     */
    public function newAction(Request $request)
    {
        $post = new Post();
        $post->setAuthor($this->getUser());

        // See http://symfony.com/doc/current/book/forms.html#submitting-forms-with-multiple-buttons
        $form = $this->createForm(PostType::class,$post)
            ->add('saveAndCreateNew',SubmitType::class);

        $form->handleRequest($request);

        // the isSubmitted() method is completely optional because the other
        // isValid() method already checks whether the form is submitted.
        // However,we explicitly add it to improve code readability.
        // See http://symfony.com/doc/current/best_practices/forms.html#handling-form-submits
        if ($form->isSubmitted() && $form->isValid()) {
            $post->setSlug($this->get('slugger')->slugify($post->getTitle()));

            $entityManager = $this->getDoctrine()->getManager();
            $entityManager->persist($post);
            $entityManager->flush();

            // Flash messages are used to notify the user about the result of the
            // actions. They are deleted automatically from the session as soon
            // as they are accessed.
            // See http://symfony.com/doc/current/book/controller.html#flash-messages
            $this->addFlash('success','post.created_successfully');

            if ($form->get('saveAndCreateNew')->isClicked()) {
                return $this->redirectToRoute('admin_post_new');
            }

            return $this->redirectToRoute('admin_post_index');
        }

        return $this->render('admin/blog/new.html.twig',[
            'post' => $post,'form' => $form->createView(),]);
    }

    /**
     * Finds and displays a Post entity.
     *
     * @Route("/{id}",requirements={"id": "\d+"},name="admin_post_show")
     * @Method("GET")
     */
    public function showAction(Post $post)
    {
        // This security check can also be performed
        // using an annotation: @Security("is_granted('show',post)")
        $this->denyAccessUnlessGranted('show',$post,'Posts can only be shown to their authors.');

        return $this->render('admin/blog/show.html.twig',]);
    }

    /**
     * Displays a form to edit an existing Post entity.
     *
     * @Route("/{id}/edit",name="admin_post_edit")
     * @Method({"GET","POST"})
     */
    public function editAction(Post $post,Request $request)
    {
        $this->denyAccessUnlessGranted('edit','Posts can only be edited by their authors.');

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

        $form = $this->createForm(PostType::class,$post);

        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $post->setSlug($this->get('slugger')->slugify($post->getTitle()));
            $entityManager->flush();

            $this->addFlash('success','post.updated_successfully');

            return $this->redirectToRoute('admin_post_edit',['id' => $post->getId()]);
        }

        return $this->render('admin/blog/edit.html.twig',]);
    }

    /**
     * Deletes a Post entity.
     *
     * @Route("/{id}/delete",name="admin_post_delete")
     * @Method("POST")
     * @Security("is_granted('delete',post)")
     *
     * The Security annotation value is an expression (if it evaluates to false,* the authorization mechanism will prevent the user accessing this resource).
     */
    public function deleteAction(Request $request,Post $post)
    {
        if (!$this->isCsrfTokenValid('delete',$request->request->get('token'))) {
            return $this->redirectToRoute('admin_post_index');
        }

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

        // Delete the tags associated with this blog post. This is done automatically
        // by Doctrine,except for SQLite (the database used in this application)
        // because foreign key support is not enabled by default in SQLite
        $post->getTags()->clear();

        $entityManager->remove($post);
        $entityManager->flush();

        $this->addFlash('success','post.deleted_successfully');

        return $this->redirectToRoute('admin_post_index');
    }
}

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...