跳到内容

安全

编辑此页

EasyAdmin 依赖于 Symfony Security 来处理所有与安全相关的事情。因此,在限制对后端某些部分的访问之前,您需要在 Symfony 应用程序中正确设置安全性

  1. 在您的应用程序中创建用户 并为其分配适当的权限(例如 ROLE_ADMIN);
  2. 定义一个防火墙,该防火墙覆盖后端的 URL。

已登录用户信息

访问受保护的后端时,EasyAdmin 会显示已登录应用程序的用户的详细信息,以及包含“注销”等选项的菜单。阅读 用户菜单参考 以获取更多详细信息。

限制对整个后端的访问

使用 access_control 选项,您可以告诉 Symfony 要求某些权限才能浏览与后端关联的 URL。这很简单,因为 仪表板路由共享一个公共前缀

1
2
3
4
5
6
7
8
# config/packages/security.yaml
security:
    # ...

    access_control:
        # change '/admin' by the prefix used by your Dashboard URLs
        - { path: ^/admin, roles: ROLE_ADMIN }
        # ...

或者,您可以使用 #[IsGranted] 属性。但是,这可能很麻烦,因为您必须将其应用于所有仪表板控制器和所有 CRUD 控制器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// app/Controller/Admin/DashboardController.php
use EasyCorp\Bundle\EasyAdminBundle\Attribute\AdminDashboard;
use EasyCorp\Bundle\EasyAdminBundle\Config\Dashboard;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractDashboardController;
use Symfony\Component\Security\Http\Attribute\IsGranted;

#[AdminDashboard(routePath: '/admin', routeName: 'admin')]
#[IsGranted('ROLE_ADMIN')]
class DashboardController extends AbstractDashboardController
{
    // ...
}

// don't forget to also apply #[IsGranted('ROLE_ADMIN')] to all CRUD controllers

限制对某些 CRUD 控制器的访问

当使用多个 Dashboard 时,您可能需要限制每个仪表板可以访问哪些 CRUD 控制器

假设在您的应用程序中,您有两个仪表板(员工使用的 DashboardController 和外部协作者使用的 GuestDashboardController)。在访客仪表板中,您只想允许与您的博客相关的某些操作。

但是,当使用 漂亮的后台管理 URL 时,EasyAdmin 将为所有仪表板中的所有 CRUD 控制器生成路由。这意味着将会有不需要的路由,例如 admin_guest_invoiceadmin_guest_user_detail 等。限制每个仪表板可以通过哪些 CRUD 控制器访问的最佳方法是使用 #[AdminDashboard] 属性

1
2
3
4
5
6
7
8
9
10
11
12
// app/Controller/Admin/DashboardController.php
use EasyCorp\Bundle\EasyAdminBundle\Attribute\AdminDashboard;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractDashboardController;

#[AdminDashboard(routePath: '/admin', routeName: 'admin', allowedControllers: [
    BlogPostCrudController::class,
    BlogCategoryCrudController::class,
])]
class DashboardController extends AbstractDashboardController
{
    // ...
}

allowedControllers 选项定义了唯一可以通过 Symfony 路由在仪表板中使用的 CRUD 控制器。实际上,上述配置将使 EasyAdmin 仅生成路由 admin_guest_blog_post_*admin_guest_blog_category_*,跳过所有其他允许访问其他控制器的路由。

提示

您还可以定义相反的选项 (deniedControllers) 以允许除该列表中包含的控制器之外的所有控制器。

限制对菜单项的访问

使用 setPermission() 方法定义用户必须具有的安全权限才能看到菜单项

1
2
3
4
5
6
7
8
9
public function configureMenuItems(): iterable
{
    return [
        // ...

        MenuItem::linkToCrud('Blog Posts', null, BlogPost::class)
            ->setPermission('ROLE_EDITOR'),
    ];
}

注意

此权限仅显示/隐藏菜单项。与这些菜单项关联的操作仍然可执行,即使用户看不到菜单项。使用 操作权限 来限制对这些操作的访问。

如果您的需求更高级,请记住仪表板类是一个常规的 Symfony 控制器,因此您可以使用任何与安全相关的服务来评估复杂表达式。在这些情况下,使用替代菜单项定义更方便,而不必处理数组合并

1
2
3
4
5
6
7
8
9
10
public function configureMenuItems(): iterable
{
    yield MenuItem::linkToDashboard('Dashboard', 'fa fa-home');

    if ($this->isGranted('ROLE_EDITOR') && '...') {
        yield MenuItem::linkToCrud('Blog Posts', null, BlogPost::class);
    }

    // ...
}

限制对操作的访问

使用 setPermission() 方法定义查看操作链接/按钮所需的安全权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;

public function configureActions(Actions $actions): Actions
{
    $viewInvoice = Action::new('invoice', 'View invoice', 'fa fa-file-invoice')
        ->linkToCrudAction('renderInvoice');

    return $actions
        // ...
        ->add(Crud::PAGE_DETAIL, $viewInvoice)
        // use the 'setPermission()' method to set the permission of actions
        // (the same permission is granted to the action on all pages)
        ->setPermission('invoice', 'ROLE_FINANCE')

        // you can set permissions for built-in actions in the same way
        ->setPermission(Action::NEW, 'ROLE_ADMIN')
    ;
}

限制对字段的访问

有几种选项可以根据登录用户限制页面中显示的信息。首先,您可以使用 setPermission() 方法显示/隐藏整个字段

1
2
3
4
5
6
7
8
9
10
11
12
public function getFields(string $action): iterable
{
    return [
        IdField::new('id'),
        TextField::new('price'),
        IntegerField::new('stock'),
        // users must have this permission/role to see this field
        IntegerField::new('sales')->setPermission('ROLE_ADMIN'),
        FloatField::new('commission')->setPermission('ROLE_FINANCE'),
        // ...
    ];
}

您还可以使用 setEntityPermission() 方法限制用户可以在 indexdetail 页面中查看的项目。此值作为调用 is_granted($permissions, $item) 函数的第一个参数传递,以确定当前用户是否可以查看给定的项目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
namespace App\Controller\Admin;

use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;

class ProductCrudController extends AbstractCrudController
{
    // ...

    public function configureCrud(Crud $crud): Crud
    {
        return $crud
            ->setEntityPermission('ROLE_ADMIN')
            // ...
        ;
    }
}

detail 页面中,如果用户没有权限,他们将看到适当的错误消息(您将在应用程序日志中看到详细的错误消息)。

index 页面中,为避免混淆和分页错误,如果用户没有权限查看某些项目,则将在列表底部显示一个空行,并显示一条消息,解释他们没有足够的权限查看某些项目

Index page with some results hidden because user does not have enough permissions

使用表达式限制访问

4.9.0

表达式支持在 EasyAdmin 4.9.0 中引入。

Symfony ExpressionLanguage 组件 允许使用简单表达式定义复杂的配置逻辑。在 EasyAdmin 中,所有 setPermission() 方法都允许传递不仅包含某些安全角色名称(例如 ROLE_ADMIN)的字符串,还允许传递完整的 Expression 对象。

首先,使用 Composer 在您的项目中安装该组件

1
$ composer require symfony/expression-language

现在,您可以像这样将 Symfony Expression 对象传递给任何 setPermission() 方法

1
2
3
4
use Symfony\Component\ExpressionLanguage\Expression;

MenuItem::linkToCrud('Restricted menu-item', null, Example::class)
    ->setPermission(new Expression('"ROLE_DEVELOPER" in role_names and "ROLE_EXTERNAL" not in role_names'));

表达式支持定义更详细的权限,基于多个角色名称、用户属性或给定主题。表达式可以包含以下任何变量

  • user - 当前用户对象
  • role_names - 当前用户的所有角色作为数组
  • subjectobject - 当前正在检查的主题
  • token - 身份验证令牌
  • trust_resolver - 身份验证信任解析器
  • auth_checker - 授权检查器服务的实例

自定义安全投票器

EasyAdmin 实现了 Symfony 安全投票器 来检查为操作、实体、菜单项等定义的权限。实际的安全权限在 EasyCorp\Bundle\EasyAdminBundle\Security\Permission 类中定义为常量(例如 Permission::EA_EXECUTE_ACTION, Permission::EA_VIEW_MENU_ITEM, 等)

如果您为后端定义了自定义安全投票器,请考虑更改应用程序使用的 访问决策策略。默认策略称为 affirmative,只要有一个投票器授予访问权限,就授予访问权限(如果 EasyAdmin 投票器授予访问权限,您的自定义投票器将无法拒绝它)。

这就是为什么您应该将默认策略更改为 unanimous,只有在没有投票器拒绝访问时才授予访问权限

1
2
3
4
# config/packages/security.yaml
security:
    access_decision_manager:
        strategy: unanimous
这项工作,包括代码示例,均根据 Creative Commons BY-SA 3.0 许可获得许可。
目录
    版本