Security Voters обеспечивают механизм для настройки мелких ограничений в приложениях Symfony. Основное преимущество перед ACL, является то, что они на порядок проще настраеваемы, настроил и используешь.
В предыдущих версиях Symfony, вотеры реализовал интерфейс VoterInterface, который имел следующую форму:
interface VoterInterface { public function supportsAttribute($attribute); public function supportsClass($class); public function vote(TokenInterface $token, $object, array $attributes); }
Реализация этого интерфейса довольно простая, но в результате код получается, как правило, немного раздутой, это можно продемонстрировать следующими 83 строками кода, необходимого для определения простого вотера:
// src/Acme/DemoBundle/Security/Authorization/Voter/PostVoter.php namespace Acme\DemoBundle\Security\Authorization\Voter; use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\User\UserInterface; class PostVoter implements VoterInterface { const VIEW = 'view'; const EDIT = 'edit'; public function supportsAttribute($attribute) { return in_array($attribute, array( self::VIEW, self::EDIT, )); } public function supportsClass($class) { $supportedClass = 'Acme\DemoBundle\Entity\Post'; return $supportedClass === $class || is_subclass_of($class, $supportedClass); } /** * @var \Acme\DemoBundle\Entity\Post $post */ public function vote(TokenInterface $token, $post, array $attributes) { // check if class of this object is supported by this voter if (!$this->supportsClass(get_class($post))) { return VoterInterface::ACCESS_ABSTAIN; } // check if the voter is used correct, only allow one attribute // this isn't a requirement, it's just one easy way for you to // design your voter if(1 !== count($attributes)) { throw new \InvalidArgumentException( 'Only one attribute is allowed for VIEW or EDIT' ); } // set the attribute to check against $attribute = $attributes[0]; // check if the given attribute is covered by this voter if (!$this->supportsAttribute($attribute)) { return VoterInterface::ACCESS_ABSTAIN; } // get current logged in user $user = $token->getUser(); // make sure there is a user object (i.e. that the user is logged in) if (!$user instanceof UserInterface) { return VoterInterface::ACCESS_DENIED; } switch($attribute) { case self::VIEW: // the data object could have for example a method isPrivate() // which checks the Boolean attribute $private if (!$post->isPrivate()) { return VoterInterface::ACCESS_GRANTED; } break; case self::EDIT: // we assume that our data object has a method getOwner() to // get the current owner user entity for this data object if ($user->getId() === $post->getOwner()->getId()) { return VoterInterface::ACCESS_GRANTED; } break; } return VoterInterface::ACCESS_DENIED; } }
В результате этой Symfony DX initiative, Symfony 2.6 позволит определить более простые security voters. Чтобы сделать это, используйте новый класс AbstractVoter который реализует VoterInterface и определяет следующие методы:
abstract class AbstractVoter implements VoterInterface { public function supportsAttribute($attribute); public function supportsClass($class); public function vote(TokenInterface $token, $object, array $attributes); abstract protected function getSupportedClasses(); abstract protected function getSupportedAttributes(); abstract protected function isGranted($attribute, $object, $user = null); }
Три метода supportsAttribute (), supportsClass () и vote() помогут вам уменьшить шаблонный код вотера и позволить вам сосредоточиться на конкретной бизнес-логике приложения. В результате избиратель, как показано выше, теперь занимает только 41 строку кода:
// src/Acme/DemoBundle/Security/Authorization/Voter/PostVoter.php namespace Acme\DemoBundle\Security\Authorization\Voter; use Symfony\Component\Security\Core\Authorization\Voter\AbstractVoter; use Symfony\Component\Security\Core\User\UserInterface; class PostVoter extends AbstractVoter { const VIEW = 'view'; const EDIT = 'edit'; protected function getSupportedAttributes() { return array(self::VIEW, self::EDIT); } protected function getSupportedClasses() { return array('Acme\DemoBundle\Entity\Post'); } protected function isGranted($attribute, $post, $user = null) { // make sure there is a user object (i.e. that the user is logged in) if (!$user instanceof UserInterface) { return false; } // custom business logic to decide if the given user can view // and/or edit the given post if ($attribute == self::VIEW && !$post->isPrivate()) { return true; } if ($attribute == self::EDIT && $user->getId() === $post->getOwner()->getId()) { return true; } return false; } }
Писать меньше кода чтобы получить те же результаты, это способ повысить вашу производительность. Это наша цель с момента введения DX initiative, и Symfony 2.6 будет первая версия реализовывающая этот новый философию.
Источник: http://symfony.com/blog/new-in-symfony-2-6-simpler-security-voters