实体提供者不在security.yml中运行php symfony3.4({“code”:401,“message”:“bad credentials”}。)

时间:2018-03-17 11:34:22

标签: php mongodb login jwt symfony-3.4

我正在使用带有DoctrineMongoDBBundle和LexikJWTAuthenticationBundle的symfony 3.4。我正在尝试创建一个返回JWT令牌的用户登录。如果我在in_memory提供者下指定用户名和密码,它将返回令牌但如果我使用实体提供者,则返回{“code”:401,“message”:“bad credentials”}。

这是我的security.yml

# To get started with security, check out the documentation:
# https://symfony.com/doc/current/security.html
security:

    # https://symfony.com/doc/current/security.html#b-configuring-how-users-are-loaded
    encoders:
        AppBundle\Document\User:
            algorithm: bcrypt
            cost: 12

    providers:  
            webprovider:
                entity:
                    class: AppBundle\Document\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

        
        login:

            pattern:  ^/api/login
            stateless: true
            anonymous: true
            provider: webprovider
            form_login:
                username_parameter: username
                password_parameter: password
                check_path:               /api/login_check
                success_handler:          lexik_jwt_authentication.handler.authentication_success
                failure_handler:          lexik_jwt_authentication.handler.authentication_failure
                require_previous_session: false

        api:
            pattern:   ^/api
            stateless: true
            guard:
                authenticators:
                    - lexik_jwt_authentication.jwt_token_authenticator
    

        main:
            anonymous: ~
            # activate different ways to authenticate

            # https://symfony.com/doc/current/security.html#a-configuring-how-your-users-will-authenticate
            #http_basic: ~

            # https://symfony.com/doc/current/security/form_login_setup.html
            #form_login: ~
    



    access_control:
        - { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/api,       roles: IS_AUTHENTICATED_FULLY }

这是我的用户类

<?php

// /AppBundle/Document/User.php
namespace AppBundle\Document;

use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Bundle\MongoDBBundle\Validator\Constraints\Unique as MongoDBUnique;
use Symfony\Component\Security\Core\User\UserInterface;
/**
 * @MongoDB\Document(collection="users")
 * @MongoDBUnique(fields="email")
 */
class User implements UserInterface
{


    /**
     * @MongoDB\Id
     */
    protected $id;

    /**
     * @MongoDB\Field(type="string")
     * @Assert\Email()
     */
    protected $email;
    /**
     * @MongoDB\Field(type="string")
     * @Assert\NotBlank()
     */
    protected $username;
    /**
     * @MongoDB\Field(type="string")
     * @Assert\NotBlank()
     */
    protected $password;

    /**
     * @MongoDB\Field(type="boolean")
     */
    private $isActive;

    public function __construct()
    {
        var_dump("1");
        $this->isActive = true;
        // may not be needed, see section on salt below
        // $this->salt = md5(uniqid('', true));
    }

    public function getId()
    {

        return $this->id;
    }

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
    public function setUsername($username)
    {
        $this->username = $username;
    }

    public function getUsername()
    {
        var_dump($this->username);  
        return $this->username;
    }
    public function getSalt()
    {
        return null;
    }
    public function getPassword()
    {
        return $this->password;
    }
    public function setPassword($password)
    {
        $this->password = $password;
    }
    public function getRoles()
    {
        return array('ROLE_USER');
    }
    public function eraseCredentials()
    {
    }

}

如果有人可以提供帮助,我真的很感激,谢谢。

1 个答案:

答案 0 :(得分:1)

你应该通过在实体中扩展Base model User calss来使用FOS bundle组件,比直接使用UserInterface更好。在你的情况下问题可能是你的密码没有被编码正确。最好使用security.password_encoder进行编码。为了更好地理解,我正在分享一个示例来设置登录和生成令牌。

您的security.yml应该如下所示

`# Symfony 3.4 security.yml
security:
    encoders:
        FOS\UserBundle\Model\UserInterface: bcrypt
        Symfony\Component\Security\Core\User\User: plaintext



    role_hierarchy:
        ROLE_ADMIN:       ROLE_USER
        ROLE_SUPER_ADMIN: ROLE_ADMIN
        ROLE_API:         ROLE_USER, ROLE_MEDIC, ROLE_STUDENT



    # https://symfony.com/doc/current/security.html#b-configuring-how-users-are-loaded
    providers:
        fos_userbundle:
            id: fos_user.user_provider.username_email

        in_memory:
            memory: ~



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


        api_login:
            pattern:  ^/api/login
            stateless: true
            anonymous: true
            form_login:
                check_path:               /api/login_check
                success_handler:          lexik_jwt_authentication.handler.authentication_success
                failure_handler:          lexik_jwt_authentication.handler.authentication_failure
                require_previous_session: false

        api:
            pattern:  ^/(api)
            stateless: true  #false (not assign cookies)
            anonymous: ~
            guard:
                authenticators:
                    - lexik_jwt_authentication.jwt_token_authenticator
                provider: fos_userbundle


    access_control: .................`

您的用户类应如下所示:

// /AppBundle/Document/User.php
namespace AppBundle\Document;

use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
use FOS\UserBundle\Model\User as BaseUser;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Bundle\MongoDBBundle\Validator\Constraints\Unique as MongoDBUnique;
/**
 * @MongoDB\Document(collection="users")
 * @MongoDBUnique(fields="email")
 */
class User implements BaseUser
{


    /**
     * @MongoDB\Id
     */
    protected $id;

    /**
     * @MongoDB\Field(type="string")
     * @Assert\Email()
     */
    protected $email;
    /**
     * @MongoDB\Field(type="string")
     * @Assert\NotBlank()
     */
    protected $username;
    /**
     * @MongoDB\Field(type="string")
     * @Assert\NotBlank()
     */
    protected $password;

    /**
     * @MongoDB\Field(type="boolean")
     */
    private $isActive;

    public function __construct()
    {
        var_dump("1");
        $this->isActive = true;
        // may not be needed, see section on salt below
        // $this->salt = md5(uniqid('', true));
    }

    public function getId()
    {

        return $this->id;
    }

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
    public function setUsername($username)
    {
        $this->username = $username;
    }

    public function getUsername()
    {
        var_dump($this->username);  
        return $this->username;
    }
    public function getSalt()
    {
        return null;
    }
    public function getPassword()
    {
        return $this->password;
    }
    public function setPassword($password)
    {
        $this->password = $password;
    }
    public function getRoles()
    {
        return array('ROLE_USER');
    }
    public function eraseCredentials()
    {
    }

}

现在你的Api tokenController.php生成令牌并验证令牌

namespace \your_namespace\Api;

use FOS\RestBundle\Context\Context;
use FOS\RestBundle\Controller\Annotations as Rest;
use FOS\RestBundle\Controller\FOSRestController;


use AppBundle\Document\User;
# Below most of the components belongs to Nelmio api or Sensio or Symfony
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Security\Core\Encoder\EncoderFactory;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;

use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Lexik\Bundle\JWTAuthenticationBundle\Exception\ExpiredTokenException;
use Lexik\Bundle\JWTAuthenticationBundle\Exception\InvalidTokenException;
use Lexik\Bundle\JWTAuthenticationBundle\Exception\JWTDecodeFailureException;
use Lexik\Bundle\JWTAuthenticationBundle\Security\Authentication\Token\PreAuthenticationJWTUserToken;

use Nelmio\ApiDocBundle\Annotation\ApiDoc;


    class TokenController extends FOSRestController
    {
        /**
         * @Rest\Post("/tokens", name="api_token_new",
         *     options={"method_prefix" = false},
         *     defaults={"_format"="json"}
         * )
         *
         * @ApiDoc(
         *    section     = "Security",
         *    description = "Get user token",
         *    parameters={
         *      {"name"="username", "dataType"="string", "required"=true, "description"="username or email"},
         *      {"name"="pass",     "dataType"="string", "required"=true, "description"="password "},
         *    }
         * )
         */
        public function newTokenAction(Request $request)
        {
            $em = $this->getDoctrine()->getManager();

            $username = $request->get('username');
            /** @var User $user */
            $user = $em->getRepository('AppBundle:User')->findOneBy(['email' => $username]);
            if (!$user) {
                throw $this->createNotFoundException();
            }
            if (!$user->isEnabled()){
                throw $this->createNotFoundException($this->get('translator')->trans('security.user_is_disabled'));
            }


            $pass = $request->get('pass');
            $isValid = $this->get('security.password_encoder')->isPasswordValid($user, $pass);
            if (!$isValid) {
                throw new BadCredentialsException();
            }

            $token = $this->get('lexik_jwt_authentication.encoder')->encode([
                'username' => $user->getUsername(),
                'id'       => $user->getId(),
                'roles'    => $user->getRoles(),
                'exp'      => time() + (30 * 24 * 3600) // 30 days expiration -> move to parameters or config
            ]);


            // Force login
            $tokenLogin = new UsernamePasswordToken($user, $pass, "public", $user->getRoles());
            $this->get("security.token_storage")->setToken($tokenLogin);

            // Fire the login event
            // Logging the user in above the way we do it doesn't do this automatically
            $event = new InteractiveLoginEvent($request, $tokenLogin);
            $this->get("event_dispatcher")->dispatch("security.interactive_login", $event);

            $view = $this->view([
                'token' => $token,
                'user'  => $user
            ]);
            $context = new Context();
            $context->addGroups(['Public']);
            $view->setContext($context);

            return $this->handleView($view);
        }


         /**
 * @Rest\Post("/validate", name="api_token_validate",
 *     options={"method_prefix" = false},
 *     defaults={"_format"="json"}
 * )
 *
 * @ApiDoc(
 *    section     = "Security",
 *    description = "Get user by token",
 *    parameters={
 *      {"name"="token", "dataType"="textarea", "required"=true, "description"="token"},
 *    }
 * )
 */
public function validateUserToken(Request $request)
{
    $token = $request->get('token');
    //get UserProviderInterface
    $fos  = $this->get('fos_user.user_provider.username_email');
    //create PreAuthToken
    $preAuthToken = new PreAuthenticationJWTUserToken($token);
    try {
        if (!$payload = $this->get('lexik_jwt_authentication.jwt_manager')->decode($preAuthToken)) {
            throw new InvalidTokenException('Invalid JWT Token');
        }
        $preAuthToken->setPayload($payload);
    } catch (JWTDecodeFailureException $e) {
        if (JWTDecodeFailureException::EXPIRED_TOKEN === $e->getReason()) {
            throw new ExpiredTokenException();
        }
        throw new InvalidTokenException('Invalid JWT Token', 0, $e);
    }
    //get user
    /** @var User $user */
    $user = $this->get('lexik_jwt_authentication.security.guard.jwt_token_authenticator')->getUser($preAuthToken, $fos);
    $view = $this->view([
        'token' => $token,
        'user'  => $user
    ]);
    $context = new Context();
    $context->addGroups(array_merge(['Public'],$user->getRoles()));
    $view->setContext($context);
    return $this->handleView($view);
}

如果你遇到任何问题,请告诉我。