SonataAdminBundle 3.102+无法重写checkAccess时如何按对象状态控权限
SonataAdminBundle 3.102+ 基于对象状态的访问控制解决方案
方案1:使用Sonata官方提供的权限检查事件
Sonata在hasAccess和checkAccess被标记为final的同时,新增了sonata.admin.event.permission.check事件,你可以通过监听该事件实现自定义权限逻辑,该逻辑会在所有权限校验阶段触发,包括模板层调用hasAccess判断按钮可见性的场景,也对超级管理员生效。
实现步骤:
- 第一步:创建事件订阅器
<?php // src/EventSubscriber/TaskPermissionSubscriber.php namespace App\EventSubscriber; use Sonata\AdminBundle\Event\PermissionCheckEvent; use Sonata\AdminBundle\Event\SonataAdminEvents; use App\Entity\Task; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Security\Core\Exception\AccessDeniedException; class TaskPermissionSubscriber implements EventSubscriberInterface { public static function getSubscribedEvents(): array { return [ SonataAdminEvents::PERMISSION_CHECK => 'onPermissionCheck', ]; } public function onPermissionCheck(PermissionCheckEvent $event): void { $admin = $event->getAdmin(); // 只处理TaskAdmin的权限检查 if (!$admin->getClass() === Task::class) { return; } $action = $event->getAction(); $object = $event->getObject(); // 校验编辑操作的对象状态 if ('edit' === $action && $object instanceof Task && $object->isClosed()) { // 如果是checkAccess调用的事件,直接抛出拒绝异常 if ($event->isThrowsException()) { throw new AccessDeniedException('Access Denied to action edit because task is closed.'); } // 如果是hasAccess调用的事件,直接设置权限结果为false $event->setAccessGranted(false); } } }
- 第二步:如果你的项目开启了服务自动装配,订阅器会自动生效;如果未开启自动装配,需要在服务配置中给订阅器添加
kernel.event_subscriber标签。
方案2:为TaskAdmin自定义权限处理器
你也可以为指定Admin单独配置权限处理器,完全控制权限判断逻辑,无需修改全局权限规则:
实现步骤:
- 第一步:创建自定义权限处理器,继承Sonata默认的
SecurityHandler
<?php // src/Security/TaskSecurityHandler.php namespace App\Security; use Sonata\AdminBundle\Security\Handler\SecurityHandler as BaseSecurityHandler; use App\Entity\Task; class TaskSecurityHandler extends BaseSecurityHandler { public function isGranted($admin, $attributes, $object = null): bool { // 先调用父类逻辑拿到基础权限结果 $baseGranted = parent::isGranted($admin, $attributes, $object); if (!$baseGranted) { return false; } // 处理自定义对象状态校验逻辑 if (\in_array('EDIT', (array)$attributes, true) && $object instanceof Task && $object->isClosed()) { return false; } return $baseGranted; } }
- 第二步:在
services.yaml中给TaskAdmin绑定自定义权限处理器:
services: app.admin.security.task_handler: class: App\Security\TaskSecurityHandler arguments: $security: '@Symfony\Component\Security\Core\Security' $superAdminRoles: ['%sonata.admin.security.super_admin_role%'] admin.task: class: App\Admin\TaskAdmin arguments: [~, App\Entity\Task, ~] tags: - { name: sonata.admin, manager_type: orm, label: Task } calls: - [ setSecurityHandler, [ '@app.admin.security.task_handler' ] ]
方案对比
- 事件方案更轻量,适合仅需修改少量Admin权限逻辑的场景,不需要额外服务配置
- 自定义权限处理器方案逻辑更内聚,适合多操作、规则复杂的权限校验场景
内容的提问来源于stack exchange,提问作者mleko64
相关产品推荐
相关产品推荐

