Symfony路由访问检查

时间:2018-09-12 15:26:32

标签: symfony security

我有一个用Symfony 3.4创建的网站,在执行操作时,我必须检查当前用户是否可以编辑目标产品,如下所示:

/**
 * @Route("/products/{id}/edit")
 */
public function editAction(Request $request, Product $product)
{
    // security
    $user = $this->getUser();
    if ($user != $product->getUser()) {
        throw $this->createAccessDeniedException();
    }

    // ...
}

如何避免对每个动作进行相同的检查(如果使用注释和表达式,则要加分)?

我已经在使用security.yml和access_control来拒绝基于角色的访问。

2 个答案:

答案 0 :(得分:2)

您可以将Voter用于此特定目的。不涉及魔术。创建和注册选民后,将在安全层中自动完成选民身份验证。

您只需要创建Voter类,然后将其注册为服务即可。但是,如果您使用默认的services.yaml配置,则将自动为您注册为服务!

这是您可以使用的示例。您可能需要更改一些项目,但是基本上就是这样。

要了解更多信息,请访问:https://symfony.com/doc/current/security/voters.html

<?php
namespace AppBundle\Security;

use AppBundle\Entity\Product;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use AppBundle\Entity\User;

class ProductVoter extends Voter
{
    const EDIT = 'EDIT_USER_PRODUCT';

    protected function supports($attribute, $subject)
    {
        if($attribute !== self::EDIT) {
            return false;
        }
        if(!$subject instanceof Product) {
            return false;
        }
        return true;
    }

    protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
    {
        /** @var Product $product */
        $product= $subject;

        $user = $token->getUser();

        if (!$user instanceof User) {
            // the user must be logged in; if not, deny access
            return false;
        }

        return $this->belongsToUser($product, $user);
    }

    private function belongsToUser(Product $product, User $user)
    {
        return $user->getId() === $product->getUser()->getId();
    }
}

答案 1 :(得分:1)

您可以尝试使用听众:

  1. 检查操作名称,例如,如果它是“ edit_product”,则继续。
  2. 获取当前登录的用户。
  3. 获取产品实体的用户。
  4. 检查当前用户是否与Product用户不同,如果为true,则抛出CreateAccessDeniedException。

services.yml

app.user.listener:
    class: AppBundle\EventListener\ValidateUserListener        
    tags:
        - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
    arguments: ["@service_container", "@doctrine.orm.entity_manager"]

编辑操作:

为操作添加了名称“ edit_product”。

/**
* 
* @Route("/products/{id}/edit",name="edit_product")
*/
public function editAction()
{
 ...

src \ AppBundle \ EventListener \ ValidateUserListener.php

<?php

namespace AppBundle\EventListener;

use Symfony\Component\HttpKernel\Event\GetResponseEvent;

class  ValidateUserListener
{   
  private $container;
  private $entityManager;

 public function __construct($container, $entityManager)
 {     
    $this->container = $container;
    $this->entityManager = $entityManager;
 }

 public function onKernelRequest(GetResponseEvent $event)
 {
    $currentRoute = $event->getRequest()->attributes->get('_route');
    if($currentRoute=='edit_product' || $currentRoute=='edit_item' )
    {
        $array_user = $this->getCurrentUser(); 
        if($array_user['is_auth'])
        {
            $current_user = $array_user['current_user'];   

            $product = $this->entityManager->getRepository('AppBundle:User')->findOneByUsername($current_user);

            $product_user = $product->getUsername();

            if ($current_user !==$product_user)
            {
                throw $this->createAccessDeniedException();
            }
        }            
    }        
  }

private function getCurrentUser() 
{
    //Get the current logged User
    $user  =  $this->container->get('security.token_storage')->getToken()->getUser();
    if(null!=$user)
    {
        //If user is authenticated
        $isauth = $this->container->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY');
        return  array('is_auth'=>$isauth, 'current_user'=>$user);
    }
    return  array('is_auth'=>false, 'current_user'=>$user);    
   }
 }

在Symfony 3.3中进行了测试