dongxieli3839
2017-11-16 20:16
浏览 62
已采纳

如何避免通过JSON在Symfony 3中返回用户密码

I am developing a Symfony app with a REST API integrated but I'm facing a problem, when returning an user entity as JSON through an API request it returns the user password and despite being encrypted I would like to avoid it.

My user entity is:

<?php

namespace AppBundle\Entity;

use AppBundle\Util\Language;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;

/**
 * @ORM\Table(name="users")
 * @ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
 */
class User implements AdvancedUserInterface, \Serializable
{
    public function __construct()
    {
        $this->isActive = true;
    }


    // Functions and parameters

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

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

    // More functions and parameters

    /** @see \Serializable::serialize() */
    public
    function serialize()
    {
        return serialize(array(
            $this->id,
            $this->username,
            $this->password,
            $this->isActive,
            $this->createdAt,
            $this->lastLogin,
        ));
    }

    /** @see \Serializable::unserialize() */
    public
    function unserialize($serialized)
    {
        list (
            $this->id,
            $this->username,
            $this->password,
            $this->isActive,
            $this->createdAt,
            $this->lastLogin,
            ) = unserialize($serialized);
    }
}

The User repository

<?php
namespace AppBundle\Repository;

use Symfony\Bridge\Doctrine\Security\User\UserLoaderInterface;
use Doctrine\ORM\EntityRepository;

class UserRepository extends EntityRepository implements UserLoaderInterface
{
    public function loadUserByUsername($username)
    {
        return $this->createQueryBuilder('u')
            ->where('u.username = :username OR u.email = :email')
            ->setParameter('username', $username)
            ->setParameter('email', $username)
            ->getQuery()
            ->getOneOrNullResult();
    }
}

I have an static method to build API responses

public static function createSuccessfulresponse($entity, $entityName, $responseCode, $userLocale = "en", $responseMsg = "")
{
    $defResponseMsg = ($responseMsg != "" ? $responseMsg : ApiResponseCode::getMsg($responseCode, $userLocale));
    $responseArray = array();
    $responseArray['responseCode'] = $responseCode;
    $responseArray['responseMsg'] = $defResponseMsg;
    $responseArray['userLocale'] = $userLocale;
    if ($entity != null) {
        $responseArray[$entityName] = $entity;
    }
    return ApiResponseHelper::serializeResponse($responseArray);
}

The response serializer

private static function serializeResponse($responseArray)
{
    $encoders = array(new JsonEncoder());
    $normalizers = array(new ObjectNormalizer());
    $serializer = new Serializer($normalizers, $encoders);
    return $serializer->serialize($responseArray, 'json');
}

And one of the API calls which returns an user entity (there are more)

/**
 * @Route("/api/url/{uid}" )
 * @Method({"GET"})
 */
public function getByUidAction($uid)
{
    $user = $this->get('security.token_storage')->getToken()->getUser();
    $entityManager = $this->getDoctrine()->getManager();
    $entity = $entityManager->getRepository('AppBundle:Workday')->findOneBy(['uid' => $uid, 'user' => $user]);
    if($entity != null){
        return new Response(ApiResponseHelper::createSuccessfulresponse($entity, "workday", ApiResponseCode::SUCCESS_FETCH_WORKDAY, $user->getLocale()));
    }else{
        return new Response(ApiResponseHelper::createSuccessfulresponse(null, "workday", ApiResponseCode::ERROR_EXISTS_WORKDAY, $user->getLocale()));
    }
}

This is one JSON response from the above method

{
    "responseCode": "successfulResponseCode",
    "responseMsg": "Data received",
    "userLocale": "es",
    "workday": {
        "id": 10,
        ... so many data
        "job": {
            "id": 11,
            .. more json data
        },
        "user": {
            "username": "amendez",
            "password": "encrypted_password",
            ... more data
        },
        ... and more data
    }
}

As you can see I receive a JSON object with the user which contains its encrypted password and many other data, my goal is to avoid returning the password key and value.

Does somebody know how could I achieve it?

  • 写回答
  • 好问题 提建议
  • 追加酬金
  • 关注问题
  • 收藏
  • 邀请回答

1条回答 默认 最新

  • douyi6818 2017-11-16 20:42
    已采纳

    You would need to define the Serializer groups with the desired getters assigned. See: https://symfony.com/doc/current/components/serializer.html#attributes-groups.

    The preferred (best-practice) method would be to assign the desired groups.

    use Symfony\Component\Serializer\Annotation\Groups;
    
    class User implements AdvancedUserInterface, \Serializable
    {        
        /**
         * @Groups({"api"})
         * @return string
         */
        public function getUsername()
        {
            return $this->username;
        }
    
    
        //...    
    
        /**
         * Get password
         * @return string
         */
        public function getPassword()
        {
            return $this->password;
        }
    }
    

    Edited for easier use of Serializer service

    In your app/config/config.yml enable annotations, which in-turn enables the serializer service.

    #config.yml
    framework:
        #...
        serializer:
            enable_annotations: true
    

    Now you can call the Serializer service directly or use DI in your custom services.

    use Symfony\Component\Serializer\Encoder\JsonEncoder;
    use Symfony\Component\HttpFoundation\JsonResponse;
    
    private static function serializeResponse($responseArray)
    {
         $serializer = $this->container->get('serializer');
    
         return $serializer->serialize($responseArray, JsonEncoder::FORMAT, array(
            'groups' => array('api'),
            'json_encode_options' => JsonResponse::DEFAULT_ENCODING_OPTIONS
        ));
    }
    

    To manually use the Serializer Component with groups.

    use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
    use Doctrine\Common\Annotations\AnnotationReader;
    use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
    
    private static function serializeResponse($responseArray)
    {
        $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
    
        $normalizer = new ObjectNormalizer($classMetadataFactory);
        $encoder = new JsonEncoder();
        $serializer = new Serializer(array($normalizer), array($encoder));
    
        return $serializer->serialize($responseArray, JsonEncoder::FORMAT, array('groups' => array('api')));
    }
    

    Alternatively you should be able to set it as part of the ignored attributes. See: https://symfony.com/doc/current/components/serializer.html#ignoring-attributes

    private static function serializeResponse($responseArray)
    {
        $normalizer = new ObjectNormalizer();
        $normalizer->setIgnoredAttributes(array('password'));
    
        $encoder = new JsonEncoder();
        $serializer = new Serializer(array($normalizer), array($encoder));
    
        return $serializer->serialize($responseArray, JsonEncoder::FORMAT);
    }
    
    评论
    解决 无用
    打赏 举报

相关推荐 更多相似问题