我已经做了类似 zIs 的相同操作(将 RoleHierarchy 存储在数据库中),但我无法像 zIs 那样在 Constructor 中加载完整的角色层次结构,因为我必须在kernel.request
事件中加载自定义原则过滤器。构造函数将在之前被调用,kernel.request
所以这对我来说是没有选择的。
因此,我检查了安全组件,发现Symfony
调用了一个自定义Voter
来roleHierarchy
根据用户角色进行检查:
namespace Symfony\Component\Security\Core\Authorization\Voter;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;
/**
* RoleHierarchyVoter uses a RoleHierarchy to determine the roles granted to
* the user before voting.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class RoleHierarchyVoter extends RoleVoter
{
private $roleHierarchy;
public function __construct(RoleHierarchyInterface $roleHierarchy, $prefix = 'ROLE_')
{
$this->roleHierarchy = $roleHierarchy;
parent::__construct($prefix);
}
/**
* {@inheritdoc}
*/
protected function extractRoles(TokenInterface $token)
{
return $this->roleHierarchy->getReachableRoles($token->getRoles());
}
}
getReachableRoles 方法返回用户可以成为的所有角色。例如:
ROLE_ADMIN
/ \
ROLE_SUPERVISIOR ROLE_BLA
| |
ROLE_BRANCH ROLE_BLA2
|
ROLE_EMP
or in Yaml:
ROLE_ADMIN: [ ROLE_SUPERVISIOR, ROLE_BLA ]
ROLE_SUPERVISIOR: [ ROLE_BRANCH ]
ROLE_BLA: [ ROLE_BLA2 ]
如果用户分配了 ROLE_SUPERVISOR 角色,则该方法返回角色 ROLE_SUPERVISOR、ROLE_BRANCH 和 ROLE_EMP(实现 RoleInterface 的角色对象或类)
此外,如果没有在security.yaml
private function createRoleHierarchy($config, ContainerBuilder $container)
{
if (!isset($config['role_hierarchy'])) {
$container->removeDefinition('security.access.role_hierarchy_voter');
return;
}
$container->setParameter('security.role_hierarchy.roles', $config['role_hierarchy']);
$container->removeDefinition('security.access.simple_role_voter');
}
为了解决我的问题,我创建了自己的自定义 Voter 并扩展了 RoleVoter-Class:
use Symfony\Component\Security\Core\Authorization\Voter\RoleVoter;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Acme\Foundation\UserBundle\Entity\Group;
use Doctrine\ORM\EntityManager;
class RoleHierarchyVoter extends RoleVoter {
private $em;
public function __construct(EntityManager $em, $prefix = 'ROLE_') {
$this->em = $em;
parent::__construct($prefix);
}
/**
* {@inheritdoc}
*/
protected function extractRoles(TokenInterface $token) {
$group = $token->getUser()->getGroup();
return $this->getReachableRoles($group);
}
public function getReachableRoles(Group $group, &$groups = array()) {
$groups[] = $group;
$children = $this->em->getRepository('AcmeFoundationUserBundle:Group')->createQueryBuilder('g')
->where('g.parent = :group')
->setParameter('group', $group->getId())
->getQuery()
->getResult();
foreach($children as $child) {
$this->getReachableRoles($child, $groups);
}
return $groups;
}
}
注意事项:我的设置类似于 zls 的设置。我对角色的定义(在我的例子中,我称之为组):
Acme\Foundation\UserBundle\Entity\Group:
type: entity
table: sec_groups
id:
id:
type: integer
generator: { strategy: AUTO }
fields:
name:
type: string
length: 50
role:
type: string
length: 20
manyToOne:
parent:
targetEntity: Group
和用户定义:
Acme\Foundation\UserBundle\Entity\User:
type: entity
table: sec_users
repositoryClass: Acme\Foundation\UserBundle\Entity\UserRepository
id:
id:
type: integer
generator: { strategy: AUTO }
fields:
username:
type: string
length: 30
salt:
type: string
length: 32
password:
type: string
length: 100
isActive:
type: boolean
column: is_active
manyToOne:
group:
targetEntity: Group
joinColumn:
name: group_id
referencedColumnName: id
nullable: false
也许这对某人有帮助。