我有几个捆绑包,想要在其中定义一些角色和层次结构。该应用程序必须以某种方式将它们合并到一个层次结构中,我想强制以某种方式使用此层次结构安全捆绑包。
我尝试做的事情:
CoreBundle
类,并标记了所有具有角色元数据的类:class CoreBundle extends AbstractBundle
{
public function build(ContainerBuilder $container): void
{
parent::build($container);
$container->addCompilerPass(new RoleHierarchyPass());
}
public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void
{
$container->import('../config/services.yaml');
$builder->registerForAutoconfiguration(RoleHierarchyInterface::class)->addTag(RoleHierarchyInterface::class);
}
}
RoleHierarchyInterface
很简单:interface RoleHierarchyInterface
{
public function getHierarchy(): array;
}
这是该接口实现的一个示例:
class CoreRoleHierarchy implements RoleHierarchyInterface
{
public function getHierarchy(): array
{
return [
'ROLE_DEVELOPER' => ["ROLE_USER"],
'ROLE_ADMIN' => ["ROLE_USER"]
];
}
}
我计划在未来添加更多的方法来描述角色(例如表单)。
RoleHierarchyPass
实施:class RoleHierarchyPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if ($container->hasDefinition(RoleHierarchyAggregator::class)) {
$taggedServices = $container->findTaggedServiceIds(RoleHierarchyInterface::class);
$serviceReferences = [];
foreach ($taggedServices as $serviceId => $tags) {
$container->getDefinition($serviceId);
$serviceReferences[] = new Reference($serviceId);
}
$definition = $container->findDefinition(RoleHierarchyAggregator::class);
$definition->setArguments(['$roleHierarchyServices' => $serviceReferences]);
}
}
}
RoleHierarchyAggregator
实施:readonly class RoleHierarchyAggregator
{
public function __construct(private iterable $roleHierarchyServices = [])
{
}
public function getMergedHierarchy(): array
{
$mergedHierarchy = [];
foreach ($this->roleHierarchyServices as $roleHierarchyService) {
if ($roleHierarchyService instanceof RoleHierarchyInterface) {
$mergedHierarchy = array_merge_recursive($mergedHierarchy, $roleHierarchyService->getHierarchy());
}
}
return $mergedHierarchy;
}
}
我想更改
security.yaml
role_hierarchy,但我无法在 RoleHierarchyAggregator
服务中执行此操作。我尝试通过捆绑的boot
方法来做到这一点:
public function boot()
{
$container = $this->container;
$roleHierarchyAggregator = $container->get(RoleHierarchyAggregator::class);
dd($roleHierarchyAggregator);
}
但是它抛出以下异常:
容器编译时,“FooBar\CoreBundle\Security\RoleHierarchyAggregator”服务或别名已被删除或内联。您应该将其公开,或者直接停止使用容器并使用依赖项注入。
尝试将
CompilerPass
添加到 Kernel
:
class Kernel extends BaseKernel
{
use MicroKernelTrait;
protected function build(ContainerBuilder $container): void
{
parent::build($container);
$container->addCompilerPass(new MergeRoleHierarchyPass());
}
}
class MergeRoleHierarchyPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$taggedServices = $container->findTaggedServiceIds(RoleHierarchyInterface::class);
foreach ($taggedServices as $serviceId => $tags) {
dd($container->get($serviceId));
}
dd($container->getParameter('security.role_hierarchy.roles'));
}
}
但这也会引发异常:
构建时不支持从父定义构建服务“FooBar\CoreBundle\Security\CoreRoleHierarchy”。
我不知道该怎么办。是否可以从不同的包中检索角色,合并它们然后传递给
security.yaml
?或者还有其他替代方案吗?
可以查看 PrepredExtension 来配置其他捆绑包中的安全捆绑包配置。例如,您的 CoreBundle 可以为安全包设置其角色:
$container->prependExtensionConfig('security', [
'role_hierarchy' => [
'ROLE_DEVELOPER' => ["ROLE_USER"],
'ROLE_ADMIN' => ["ROLE_USER"]
],
]);
参考:https://symfony.com/doc/current/bundles/prepend_extension.html