2年前我走上了这条路,结果证明这是一个糟糕的决定。修改 ACL 系统很困难,并且在更新 Symfony 时可能会导致问题。至少有 2 个更好的解决方案。我会将它们全部列出,以便您决定哪个最适合您的需求。
新的安全身份
我正在使用GroupInterface
FOSUserBundle 中的,但我想您也可以使用自己的。需要添加以下文件:
接下来:通过提供以下参数重新连接依赖注入容器:
<parameter key="security.acl.dbal.provider.class">
Acme\Bundle\DemoBundle\Security\Acl\Dbal\MutableAclProvider
</parameter>
<parameter key="security.acl.security_identity_retrieval_strategy.class">
Acme\Bundle\DemoBundle\Security\Acl\Domain\SecurityIdentityRetrievalStrategy
</parameter>
是时候交叉手指,看看它是否有效。由于这是旧代码,我可能忘记了一些东西。
为组使用角色
这个想法是让组名对应于角色。
一种简单的方法是让您的User
实体重新实现UserInterface::getRoles
:
public function getRoles()
{
$roles = parent::getRoles();
// This can be cached should there be any performance issues
// which I highly doubt there would be.
foreach ($this->getGroups() as $group) {
// GroupInterface::getRole() would probably have to use its
// canonical name to get something like `ROLE_GROUP_NAME_OF_GROUP`
$roles[] = $group->getRole();
}
return $roles;
}
一个可能的实现GroupInterface::getRole()
:
public function getRole()
{
$name = $this->getNameCanonical();
return 'ROLE_GROUP_'.mb_convert_case($name, MB_CASE_UPPER, 'UTF-8');
}
现在只需按照食谱文章中的说明创建所需的 ACE 。
创建选民
最后,您可以使用自定义投票器来检查特定组的存在以及用户是否有权访问所述对象。一个可能的实现:
<?php
namespace Acme\Bundle\DemoBundle\Authorization\Voter;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
class MySecureObjectVoter implements VoterInterface
{
/**
* {@inheritDoc}
*/
public function supportsAttribute($attribute)
{
$supported = array('VIEW');
return in_array($attribute, $supported);
}
/**
* {@inheritDoc}
*/
public function supportsClass($class)
{
return $class instanceof GroupableInterface;
}
/**
* {@inheritDoc}
*/
public function vote(TokenInterface $token, $object, array $attributes)
{
$result = VoterInterface::ACCESS_ABSTAIN;
if (!$object instanceof MySecureObject) {
return VoterInterface::ACCESS_ABSTAIN;
}
foreach ($attributes as $attribute) {
if (!$this->supportsAttribute($attribute)) {
continue;
}
// Access is granted, if the user and object have at least 1
// group in common.
if ('VIEW' === $attribute) {
$objGroups = $object->getGroups();
$userGroups = $token->getUser()->getGroups();
foreach ($userGroups as $userGroup) {
foreach ($objGroups as $objGroup) {
if ($userGroup->equals($objGroup)) {
return VoterInterface::ACCESS_GRANTED;
}
}
}
return voterInterface::ACCESS_DENIED;
}
}
}
}
有关选民的更多详细信息,请参阅食谱示例。
我会避免创建自定义安全身份。使用提供的其他两种方法。如果您将拥有大量记录并且每个记录都必须具有不同的访问设置,则第二种解决方案效果最好。投票者可用于设置简单的访问授权逻辑(大多数较小的系统似乎都属于这种情况)或需要灵活性时。