为许多实体使用一个类Voter

时间:2015-01-27 20:04:32

标签: php symfony symfony-security

这是Voter类中 supportsClass 的函数

http://symfony.com/doc/2.5/cookbook/security/voters_data_permission.html

 public function supportsClass($class)
{
    $supportedClass = 'AppBundle\Entity\Post';

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

我想知道是否可以为同一捆绑中的许多实体使用一个类选民,或者我必须为每个实体创建一个选民?

修改 我找到了这个解决方案:

 public function supportsClass($class)
{
    $classes = array(
        'Project\AgenceBundle\Entity\Agence',
        'Project\AgenceBundle\Entity\SubAgence',
        'Project\AgenceBundle\Entity\Subscription'
    );
    $ok = false;
    foreach($classes as $supportedClass)
    $ok = $ok || $supportedClass === $class || is_subclass_of($class, $supportedClass);

    return $ok;
}

1 个答案:

答案 0 :(得分:2)

简而言之,您可以根据需要重复使用选民。例如,您的选民可以对抗界面。

但是你不应该使用选民来判断太多东西只是为了节省几行代码。可能如果选民可以判断一些非派生类的对象,但他们有共同点。这反过来又是界面和特性​​的好地方。所以选民应该反对那个界面。 那是什么接口,给你合同。

如果您在supportsClass中有一组类,而且将来会更改其中一个类。您可能会破坏该类的Voter,但由于它不受接口限制,因此没有静态分析或PHP解释器可以捕获它。这是一个很大的问题。

正如您所看到的,Voter是由3个部分构建的。

  • supportsClass 告诉Symfony这个Voter是否可以决定某个类的对象。
  • supportsAttribute 告诉Symfony这个Voter是否可以决定此操作。
  • 投票根据传递的对象决定是否是/否/不知道

不完全它是如何工作的。但它应该让你知道选民的目的是什么。

您:

//You in controller
 if (!$this->get('security.context')->isGranted('edit', $object)) {
     throw new AuthenticationException('Not a step furher chap!');
 }

框架:

//security.context
//again it is rough idea what it does for real implementation check Symfoy github
public function isGranted($action, $object) {
  //There it goes trough all voters from all bundles!
  foreach ($this->voters as $voter) {
      if (!$voter->supportsClass(get_class($object))) {
          //this voter doesn't care about this object
          continue;
      }

      if (!$voter->supportsAttribute($action)) {
          //this voter does care about this object but not about this action on it
          continue;
      }

      //This voter is there to handle this object and action, so lest se what it has to say about it
      $answer = $voter->vote(..);
      ...some more logic
  }
}

我头顶的奇怪例子:

interface Owneable {
    public function getOwnerId();
}

trait Owned {

    /**
     * @ORM....
     */ 
    protected $ownerId;

    public function getOwnerId() {
        return $this->ownerId;
    }

    public function setOwnerId($id) {
        $this->ownerId = $id;
    }
}

class Post implements Owneable {
   use Owned;
}

class Comment implements Owneable {
   use Owned;
}

class OwnedVoter implements VoterInterface
{

    public function supportsAttribute($attribute)
    {
        return $attribute === 'edit';
    }

    public function supportsClass($class)
    {
        //same as return is_subclass_of($class, 'Owneable');
        $interfaces = class_implements($class);
        return in_array('Owneable' , $interfaces);
    }

    public function vote(TokenInterface $token, $ownedObject, array $attributes)
    {
        if (!$this->supportsClass(get_class($ownedObject))) {
            return VoterInterface::ACCESS_ABSTAIN;
        }


        if (!$this->supportsAttribute($attributes[0])) {
            return VoterInterface::ACCESS_ABSTAIN;
        }


        $user = $token->getUser();
        if (!$user instanceof UserInterface) {
            return VoterInterface::ACCESS_DENIED;
        }




        $userOwnsObject = $user->getId() === $ownedObject->getOwnerId();
        if ($userOwnsObject) {
            return VoterInterface::ACCESS_GRANTED;
        }


        return VoterInterface::ACCESS_DENIED;
    }
}

提示:Voter就像其他任何类一样,继承和抽象类等工作也在这里工作!

提示2:选民已注册为您可以将security.context或任何其他服务传递给它的服务。所以你可以很好地重用你的代码