我们在进行symfony开发时,经常会为不同的用户取一个不同的角色名,但是这些角色都有一个基础的权限。
比如ROLE_SUPER_ADMIN和ROLE_MAIN_ADMIN都是管理员,他们都有ROLE_ADMIN的所有权限,这样在路由守卫时可以用ROLE_ADMIN来做为角色限制。
所以在symfony的security.yaml中我们会用role_hierarchy来定义角色权限:
role_hierarchy:
ROLE_SUPER_ADMIN: ROLE_ADMIN
但在现实开发中,我们可能会自定义添加修改角色,把角色写入数据库,由程序使用者来定义的话,肯定不能通过修改yaml文件来实现。所以我们希望能从系统中动态设置,那么这个时候怎么处理呢?
其实很简单:我们只需要装饰security.role_hierarchy服务即可,比如我这里仅仅简单的把所有添加的角色都具有ROLE_ADMIN的权限:
<?php
namespace App\Security;
use App\Repository\RolesRepository;
use Symfony\Component\DependencyInjection\Attribute\AsDecorator;
use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;
use Symfony\Contracts\Cache\CacheInterface;
use Symfony\Contracts\Cache\ItemInterface;
#[AsDecorator('security.role_hierarchy')]
class DynamicRoleHierarchy implements RoleHierarchyInterface
{
public function __construct(
private RolesRepository $rolesRepository,
private CacheInterface $cache
)
{
}
public function getReachableRoleNames(array $roles): array
{
$reachableRoles = [];
// 获取所有角色代码
$allRoles = $this->getAllRoles();
foreach ($roles as $role) {
// 将用户本身拥有的角色添加到可达角色列表中
$reachableRoles[] = $role;
// 如果用户拥有的角色在数据库中存在,则添加 ROLE_ADMIN 权限
if (in_array($role, $allRoles)) {
$reachableRoles[] = 'ROLE_ADMIN';
}
}
return array_unique($reachableRoles);
}
private function getAllRoles(): array
{
return $this->cache->get('all_roles', function (ItemInterface $item) {
$item->expiresAfter(3600); // 缓存 1 小时
// 获取所有角色代码
$roles = $this->rolesRepository->findAll();
$roleCodes = [];
foreach ($roles as $role) {
$roleCodes[] = $role->getCode();
}
return $roleCodes;
});
}
}
代码很简单,相信我不需要过多的解释。但是如果仅仅这样做的话,你会发现在使用
isGranted("ROLE_SUPER_ADMIN")
判断角色时会没有作用,所以这里需要我们在security.yaml中这样处理:
role_hierarchy:
ROLE_ADMIN: ROLE_ADMIN
这里的ROLE_ADMIN你可以试一试其它的是否可行,我这里仅仅是把ADMIN这个最基本的角色放在这里而已,所有逻辑都可以生效。