You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 12:54:03