Symfony2 ACL 结合另一个条件
Posted
技术标签:
【中文标题】Symfony2 ACL 结合另一个条件【英文标题】:Symfony2 ACL combined with another criteria 【发布时间】:2011-12-14 07:36:30 【问题描述】:我想知道是否有人知道使用 Symfony2 ACL 系统实现此目的的优雅方法。
我有一个Comment
实体(我的域对象),需要ROLE_USER
编辑,但这只能在评论发布后的 5 分钟内进行 - 否则评论只能由 ROLE_ADMIN
编辑。
使其只能由ROLE_USER
和ROLE_ADMIN
编辑很简单,只需为每个设置一个RoleSecurityIdentity
。
现在,当我想合并ROLE_USER
的时间因素时,我的问题就出现了。我的第一个问题是它需要来自域对象的信息,而不仅仅是 ACL 表,但我认为这可以通过自定义 ObjectIdentity
类来解决,该类也可以保存 Comment
发布的时间。
现在是困难的部分
我想我需要创建一个自定义的PermissionGrantingStrategy
,它知道还要查看创建时间。这必须在检查 Comment
类型时加载,但我不知道如何加载它。有谁知道是否有某种工厂可以配置这种东西?这样,如果一个实体有一个特定的 PermissionGrantingStrategy
与之关联,那么它就会被使用,否则使用默认值?
我知道这有点长,如果有人知道如何实现这一点,非常感谢,因为 ACL 文档目前似乎有点稀疏。我的后备解决方案是简单地提供某种服务来检查评论是否可以编辑,并且根本不用 ACL。
【问题讨论】:
【参考方案1】:我发布这个解决方案是为了让其他人可以看到我的最终代码,但这里是我在按照有问题的建议实施 Voter 时发现的缺陷。
supportsAttribute:似乎当您在SecurityContext
上调用isGranted
方法时,它实际上并没有在将vote
调用委托给VoterInterface
之前检查此方法,所以在您的 vote
方法中,您实际上必须自己检查属性。
supportsClass:在上面有问题的回答中,这种方法似乎是基于工厂的选择的关键,VoterInterface
s 可以投票,但实际上 symfony2 文档显示:
supportsClass() 方法用于检查投票者是否支持当前用户令牌类。
因此,它实际上似乎与Voter
是否支持令牌类型有关。更糟糕的是,php Doc 似乎含糊不清:
检查选民是否支持给定的类。
无论如何,主要问题是在将调用委托给任何选民的vote
方法之前,SecurityContext
永远不会检查此方法 - 即使此方法被硬编码为 @987654331 @vote
仍然会被调用!
所以基本上这个故事的寓意似乎是:手动检查 $attributes
和 $object
进入 vote
方法。
我的代码:
services.yml
parameters:
comment_voter.class: Acme\Bundle\CommentBundle\Security\Authorization\Voter\CommentVoter
services:
comment_voter:
class: %comment_voter.class%
arguments: [@service_container]
public: false
tags:
- name: security.voter
和选民类别:
<?php
namespace Acme\Bundle\CommentBundle\Security\Authorization\Voter;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Acme\Bundle\CommentBundle\Entity\Comment;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* A class to check editing privileges for Comments.
*/
class CommentVoter implements VoterInterface
const AUTHOR_EDIT_TIME_LIMIT = 300;
private $container;
public function __construct($container)
$this->container = $container;
public function supportsAttribute($attribute)
return $attribute === 'EDIT';
public function supportsClass($class)
return true;
/**
* Checks whether or not the current user can edit a comment.
*
* Users with the role ROLE_COMMENT_MODERATOR may always edit.
* A comment's author can only edit within 5 minutes of it being posted.
*
* @inheritdoc
*/
public function vote(TokenInterface $token, $object, array $attributes)
if ( !($object instanceof Comment) )
return VoterInterface::ACCESS_ABSTAIN;
// Only supports 'EDIT' for now.
if ( !$this->supportsAttribute($attributes[0]) )
return VoterInterface::ACCESS_ABSTAIN;
$user = $token->getUser();
if ( !($user instanceof UserInterface) )
return VoterInterface::ACCESS_DENIED;
// Is the token a comment moderator?
if ( $this->container->get('security.context')->isGranted('ROLE_COMMENT_MODERATOR') )
return VoterInterface::ACCESS_GRANTED;
// Is the token the author of the post and within the edit window.
$originalRevision = $object->getOriginalRevision();
if ( $originalRevision->getAuthor()->equals($user) )
if (
(time() - $originalRevision->getCreationDate()->getTimestamp())
<= self::AUTHOR_EDIT_TIME_LIMIT
)
return VoterInterface::ACCESS_GRANTED;
return VoterInterface::ACCESS_DENIED;
最后是模板:
% if is_granted('EDIT', comment) %<a href="#">Edit</a>% endif %
我希望这对将来的其他人有所帮助,非常感谢 Problematic 为我指明了 Voters 的方向。
【讨论】:
感谢@Problematic 和 kasheen,这将是 Symfony 食谱github.com/symfony/symfony-docs 的一个很好的补充;) 你应该提交 PR。【参考方案2】:您是否考虑过使用选民?有一个 cookbook recipe 用于实现 IP 黑名单投票器,但可以轻松修改它以处理对评论对象的编辑检查。
您可以在Symfony\Component\Security\Acl\Voter\AclVoter
(在线here)查看默认的 AclVoter,尽管您的显然可以增加而不是替换它,并且更简单。
作为概念的快速证明:
class CommentTimestampVoter implements VoterInterface
public function supportsAttribute($attribute)
return 'edit' === $attribute;
public function vote(TokenInterface $token, $object, array $attributes)
// 1. check if $token->getUser() has ROLE_ADMIN and return VoterInterface::ACCESS_GRANTED if so
// 2. check if $token->getUser() equals $object->getAuthor() and return VoterInterface::ACCESS_DENIED if not
// 3. check that $object->getCreatedAt() is within the window allowed for editing and return VoterInterface::ACCESS_GRANTED if so
// 4. return VoterInterface::ACCESS_DENIED
public function supportsClass($class)
return 'Acme\CommentBundle\Entity\Comment' === $class;
【讨论】:
这听起来像我需要的,很好地插入并且还可以通过安全上下文的 isGranted 访问(它也可以在我的前端用于有条件地显示控件以编辑评论)。今晚我会试一试,如果可行,请勾选答案,但看起来你是赢家:) 谢谢! 好的,在实施此解决方案后,我会说这是要走的路,但我会在下面发布我的代码,以防它帮助任何人,因为我在创建选民时发现了很多陷阱,所以希望我的代码可以作为其他人的文档(尽管我不能保证正确性)。以上是关于Symfony2 ACL 结合另一个条件的主要内容,如果未能解决你的问题,请参考以下文章