Symfony2 - 为用户和类别使用控制器安全性

Posted

技术标签:

【中文标题】Symfony2 - 为用户和类别使用控制器安全性【英文标题】:Symfony2 - Using controller security for user and category 【发布时间】:2014-09-28 19:53:59 【问题描述】:

我正在尝试限制用户访问控制器中的 CRUD 访问。

我与用户和类别有双向 OneToOne 关系。设置为仅允许 1 个用户访问 1 个博客。

我正在通过以与该类别无关的另一个用户身份登录来对此进行测试。单击新建时,表单会绕过我设置的任何安全性加载。

推测问题在于传入的 $title 参数(试图将其作为路由变量传入),因为我认为我没有正确设置它。

有人可以指导我做错了什么吗?

控制器代码

/**
 * Post controller.
 *
 * @Route("/category")
 */

/**
 * Creates a new Post entity.
 *
 * @Route("/", name="category_create")
 * @Method("POST")
 * @Template("AcmeDemoBundle:Page:new.html.twig")
 */
public function createAction(Request $request, $title)

    // User security
    $em = $this->getDoctrine()->getManager();

    $categoryRepository = $em->getRepository('AcmeDemoBundle:Category');

    $category = $categoryRepository->findOneBy(array(
        'title' => '$title',
    ));

    $owner = $category->getUser();

    $currentUser = $this->get('security.context')->getToken()->getUser();

    if ($owner != $currentUser) 
        throw new AccessDeniedException('You do not have access for this');
    

    // Form creation
    $post = new Post();

    $form = $this->createCreateForm($post);
    $form->handleRequest($request);

    if ($form->isValid()) 
        $em = $this->getDoctrine()->getManager();
        $em->persist($post);
        $em->flush();

        return $this->redirect($this->generateUrl('category_show', array('id' => $post->getId())));
    

    return array(
        'post' => $post,
        'form' => $form->createView(),
    );

类别实体

/**
 * @var integer
 *
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
private $id;

/**
 * @var string
 *
 * @ORM\Column(name="title", type="string", length=255)
 */
private $title;

/**
 * @var string
 *
 * @Gedmo\Slug(fields="title", unique=false)
 * @ORM\Column(length=255)
 */
private $catslug;

/**
 * @ORM\OneToMany(targetEntity="Post", mappedBy="category")
 */
protected $posts;

/**
 * @ORM\OneToOne(targetEntity="Acme\DemoBundle\Entity\User", inversedBy="cat")
 * @ORM\JoinColumn(name="cat_id", referencedColumnName="id")
 */
protected $user;

用户实体

/**
 * @var integer
 *
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
private $id;

/**
 * @var string
 *
 * @ORM\Column(name="username", type="string", length=255)
 * @Assert\NotBlank(message="Field cannot be blank")
 */
private $username;

/**
 * @var string
 *
 * @ORM\Column(name="password", type="string", length=255)
 */
private $password;

/**
 * @ORM\Column(type="string", length=255)
 * @Assert\NotBlank()
 */
private $email;

/**
 * @ORM\Column(type="json_array")
 */
private $roles = array();

/**
 * @var bool
 *
 * @ORM\Column(type="boolean")
 */
private $isActive = true;

/**
 * @Assert\NotBlank
 * @Assert\Regex(
 *     pattern="/^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).*$/",
 *     message="Use 1 upper case letter, 1 lower case letter, and 1 number")
 */
private $plainPassword;

/**
 * @ORM\OneToOne(targetEntity="Acme\DemoBundle\Entity\Category", mappedBy="user")
 */
private $cat;

【问题讨论】:

【参考方案1】:

您可以尝试创建自己的Security Voter 以检查用户是否有权执行此操作。示例代码:

class CategoryVoter implements VoterInterface

    const CREATE = 'create';

    /**
     * @param string $attribute
     * @return bool
     */
    public function supportsAttribute($attribute)
    
        return in_array($attribute, [
            self::CREATE
        ]);
    

    /**
     * @param string $class
     * @return bool
     */
    public function supportsClass($class)
    
        $supportedClass = 'Acme\DemoBundle\Entity\Category';

        return $supportedClass === $class || is_subclass_of($class, $supportedClass);
    

    /**
     * @param TokenInterface $token
     * @param object $blog
     * @param array $attributes
     * @return int
     */
    public function vote(TokenInterface $token, Category $category, array $attributes)
    
        ....

        $attribute = $attributes[0];
        $user = $token->getUser();

        switch($attribute) 
            case 'create':
                if ($user->getId() === $category->getUser()->getId()) 
                    return VoterInterface::ACCESS_GRANTED;
                
                break;

            ....
        

        ...
    

创建动作:

public function createAction(Request $request, $title)

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

    $categoryRepository = $em->getRepository('AcmeDemoBundle:Category');

    $category = $categoryRepository->findOneBy([
        'title' => '$title',
    ]);

    ...

    if (false === $this->get('security.context')->isGranted('create', $category)) 
        throw new AccessDeniedException('Unauthorised access!');
    

    ...

【讨论】:

以上是关于Symfony2 - 为用户和类别使用控制器安全性的主要内容,如果未能解决你的问题,请参考以下文章

Symfony2,Doctrine,延迟加载和代理类问题

Symfony2 - 将安全访问控制设置为只允许匿名身份验证

多对多的 Symfony2 DQL 更新查询

Symfony2:使用 FOSUserBundle 时如何在控制器内获取用户对象?

带有自定义菜单的 Symfony2 与奏鸣曲集成

Symfony2 中的 JWT 令牌