安全性
用户管理
默认情况下,SonataAdminBundle 不附带任何用户管理功能,但是应用程序很可能需要此功能。为此,您可以尝试 SonataUserBundle
。
SonataUserBundle
在 Symfony 中增加了对数据库支持的用户系统的支持。它为用户管理提供了一个灵活的框架,旨在处理诸如用户登录、注册和密码检索之类的常见任务。
SonataUserBundle
包括
- 默认登录区域
- 默认的
user_block
模板,用于显示当前用户和注销链接 - 2 个 Admin 类:User 和 Group
- User 和 Group 的默认类。
SonataAdminBundle
中有一个小小的魔法:如果 bundle 检测到 SonataUserBundle
类,则默认的 user_block
模板将更改为使用 SonataUserBundle
提供的模板。
安装过程可在专门的 SonataUserBundle 文档区域中找到。
安全处理程序
安全部分由 SecurityHandler
管理,该 bundle 附带 3 个处理程序
sonata.admin.security.handler.role
:ROLES 用于处理权限sonata.admin.security.handler.acl
:ACL 和 ROLES 用于处理权限sonata.admin.security.handler.noop
:始终返回 true,可以与 Symfony 防火墙一起使用
默认值为 sonata.admin.security.handler.noop
,如果您想更改默认值,可以将 security_handler
设置为 sonata.admin.security.handler.acl
或 sonata.admin.security.handler.role
。
要快速保护 admin,可以使用角色安全性。它允许指定用户可以对 admin 执行的操作。ACL 安全系统更高级,允许保护对象。对于使用以前的 ACL 实现的人员,您可以将 security_handler
切换到角色安全处理程序。
配置
需要安全处理程序来确定要使用的安全类型。如果使用 ACL,则必须设置 acl_user_manager
参数,其他参数设置为默认值,如果需要,请更改它们。
使用角色
1 2 3 4 5 6 7 8
# config/packages/sonata_admin.yaml
sonata_admin:
security:
handler: sonata.admin.security.handler.role
role_admin: ROLE_ADMIN
role_super_admin: ROLE_SUPER_ADMIN
使用 ACL
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
# config/packages/sonata_admin.yaml
sonata_admin:
security:
handler: sonata.admin.security.handler.acl
# this service MUST implement ``Sonata\AdminBundle\Util\AdminAclUserManagerInterface``.
acl_user_manager: App\Manager\AclUserManager
role_admin: ROLE_ADMIN
role_super_admin: ROLE_SUPER_ADMIN
# acl security information
information:
GUEST: [VIEW, LIST]
STAFF: [EDIT, HISTORY, LIST, CREATE]
EDITOR: [OPERATOR, EXPORT]
ADMIN: [MASTER]
# permissions not related to an object instance and also to be available when objects do not exist
# the DELETE admin permission means the user is allowed to batch delete objects
admin_permissions: [CREATE, LIST, DELETE, UNDELETE, EXPORT, OPERATOR, MASTER]
# permission related to the objects
object_permissions: [VIEW, EDIT, HISTORY, DELETE, UNDELETE, OPERATOR, MASTER, OWNER]
稍后,我们将解释如何使用 SonataUserBundle
设置 ACL。
角色处理程序
sonata.admin.security.handler.role
允许您精细地操作可以执行的操作(取决于实体类),而无需设置 ACL。
配置
首先,如上所述激活角色安全处理程序。
每次用户尝试在 admin 中执行操作时,Sonata 都会检查他是否是超级管理员(ROLE_SUPER_ADMIN
或配置中指定的角色)或是否具有权限。
权限是
权限 | 描述 |
---|---|
LIST | 查看对象列表 |
VIEW | 查看一个对象的详细信息 |
CREATE | 创建一个新对象 |
EDIT | 更新现有对象 |
HISTORY | 访问对象的编辑历史记录 |
DELETE | 删除现有对象 |
EXPORT | (对于原生 Sonata 导出链接) |
ALL | 授予 LIST、VIEW、CREATE、EDIT、DELETE 和 EXPORT 权限 |
每个权限都与一个 admin 相关:如果您尝试在 FooAdmin
(声明为 app.admin.foo
服务)中获取列表,Sonata 将检查用户是否具有 ROLE_APP_ADMIN_FOO_EDIT
或 ROLE_APP_ADMIN_FOO_ALL
角色。
注意
将相同的 admin 声明为 App\Admin\FooAdmin
会导致 ROLE_APP\ADMIN\FOOADMIN_EDIT
和 ROLE_APP\ADMIN\FOOADMIN_ALL
!
角色名称将基于您的 admin 服务名称。
服务名称 | 角色名称 |
---|---|
app.admin.foo | ROLE_APP_ADMIN_FOO_{PERMISSION} |
my.blog.admin.foo_bar | ROLE_MY_BLOG_ADMIN_FOO_BAR_{PERMISSION} |
App\Admin\FooAdmin | ROLE_APP |
注意
如果您的 admin 服务命名为 my.blog.admin.foo_bar
(注意下划线 _
),它将变为:ROLE_MY_BLOG_ADMIN_FOO_BAR_{PERMISSION}
因此,我们的 security.yaml
文件可能如下所示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
# config/packages/security.yaml
security:
# ...
role_hierarchy:
# for convenience, I decided to gather Sonata roles here
ROLE_SONATA_FOO_READER:
- ROLE_SONATA_ADMIN_DEMO_FOO_LIST
- ROLE_SONATA_ADMIN_DEMO_FOO_VIEW
ROLE_SONATA_FOO_EDITOR:
- ROLE_SONATA_ADMIN_DEMO_FOO_CREATE
- ROLE_SONATA_ADMIN_DEMO_FOO_EDIT
ROLE_SONATA_FOO_ADMIN:
- ROLE_SONATA_ADMIN_DEMO_FOO_DELETE
- ROLE_SONATA_ADMIN_DEMO_FOO_EXPORT
# those are the roles I will use (less verbose)
ROLE_STAFF: [ROLE_USER, ROLE_SONATA_FOO_READER]
ROLE_ADMIN: [ROLE_STAFF, ROLE_SONATA_FOO_EDITOR, ROLE_SONATA_FOO_ADMIN]
ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
# you could alternatively use for an admin who has all rights
ROLE_ALL_ADMIN: [ROLE_STAFF, ROLE_SONATA_FOO_ALL]
# set access_strategy to unanimous, else you may have unexpected behaviors
access_decision_manager:
strategy: unanimous
请注意,我们还将 access_strategy
设置为 unanimous。这意味着如果一个投票器(例如 Sonata)拒绝访问,则访问将被拒绝。有关此主题的更多信息,请参阅 Symfony 文档中的 更改访问决策策略。
用法
您现在可以从 Admin 类测试用户是否已授权
1 2 3
if ($this->hasAccess('list')) {
// ...
}
从扩展 Sonata\AdminBundle\Controller\CRUDController
的控制器
1 2 3
if ($this->admin->hasAccess('list')) {
// ...
}
或从 Twig 模板
1 2 3
{% if admin.hasAccess('list') %}
{# ... #}
{% endif %}
请注意,您不必重新指定前缀。
Sonata 检查它在内部处理的操作的这些权限。当然,您将不得不在自己的代码中重新检查它们。
您还可以创建自己的权限,例如 EMAIL
(这将转换为角色 ROLE_APP_ADMIN_FOO_EMAIL
)。
自定义处理程序的行为
如果您想更改处理程序的行为,请创建自己的处理程序,实现 Sonata
。
并在您的配置中将其指定为 Sonata 安全处理程序
1 2 3 4 5
# config/packages/sonata_admin.yaml
sonata_admin:
default_admin_services:
security_handler: App\Security\Handler\MySecurityHandler
ACL 和 SonataUserBundle
如果您想要一种直接的方式来处理用户,请使用
- SonataUserBundle:处理存储在 RDBMS 或 MongoDB 中的用户和组。
安全集成正在进行中,并且存在一些已知问题
- ACL 权限是不可变的
- 必须实现一个侦听器,如果在 Admin 外部创建对象,则使用所需的规则创建对象访问控制列表
配置
在您可以使用 sonata-project/SonataUserBundle
之前,您需要按照 bundle 文档中的描述进行设置。
如果您要使用 ACL,则必须创建一个实现 `SonataAdminBundleUtilAdminAclUserManagerInterface` 的服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
namespace App\Manager;
use Sonata\UserBundle\Model\UserManagerInterface;
use Sonata\AdminBundle\Util\AdminAclUserManagerInterface;
final class AclUserManager implements AdminAclUserManagerInterface
{
/**
* @var UserManagerInterface
*/
private $userManager;
public function __construct(UserManagerInterface $userManager)
{
$this->userManager = $userManager;
}
public function findUsers(): iterable
{
return $this->userManager->findUsers();
}
}
然后配置 SonataAdminBundle
1 2 3 4 5 6 7
# config/packages/sonata_admin.yaml
sonata_admin:
security:
handler: sonata.admin.security.handler.acl
acl_user_manager: App\Manager\AclUserManager
# ...
SonataUserBundle 的以下配置定义了
sonata-project/SonataUserBundle
作为安全提供程序- 用于身份验证的登录表单
- 访问控制:具有相关所需角色的资源,重要的部分是 admin 配置
acl
选项以启用 ACLAdminPermissionMap
定义 Admin 类的权限
1 2 3 4 5 6 7 8 9
# config/services.yaml
services:
security.acl.permission.map:
class: Sonata\AdminBundle\Security\Acl\Permission\AdminPermissionMap
# optionally use a custom MaskBuilder
parameters:
sonata.admin.security.mask.builder.class: Sonata\AdminBundle\Security\Acl\Permission\MaskBuilder
在 config/packages/security.yaml
中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
# config/packages/security.yaml
security:
providers:
sonata_user_bundle:
id: sonata.user.security.user_provider
firewalls:
admin:
pattern: .*
form-login:
provider: sonata_user_bundle
login_path: /login
use_forward: false
check_path: /login_check
failure_path: null
logout: true
anonymous: true
access_control:
# The WDT has to be allowed to anonymous users to avoid requiring the login with the AJAX request
- { path: ^/wdt/, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/profiler/, role: IS_AUTHENTICATED_ANONYMOUSLY }
# AsseticBundle paths used when using the controller for assets
- { path: ^/js/, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/css/, role: IS_AUTHENTICATED_ANONYMOUSLY }
# URL of SonataUserBundle which need to be available to anonymous users
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/login_check$, role: IS_AUTHENTICATED_ANONYMOUSLY } # for the case of a failed login
- { path: ^/user/new$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/user/check-confirmation-email$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/user/confirm/, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/user/confirmed$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/user/request-reset-password$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/user/send-resetting-email$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/user/check-resetting-email$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/user/reset-password/, role: IS_AUTHENTICATED_ANONYMOUSLY }
# Secured part of the site
# This config requires being logged for the whole site and having the admin role for the admin part.
# Change these rules to adapt them to your needs
- { path: ^/admin/, role: ROLE_ADMIN }
- { path: ^/.*, role: IS_AUTHENTICATED_ANONYMOUSLY }
# Sonata "special" roles (ROLE_SONATA_ADMIN and ROLE_SUPER_ADMIN) are configurable
role_hierarchy:
ROLE_ADMIN: [ROLE_USER, ROLE_SONATA_ADMIN]
ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
acl:
connection: default
- 安装 ACL 表
bin/console init:acl
- 创建一个新的 root 用户
1 2 3 4 5
bin/console sonata:user:create --super-admin
Please choose a username:root
Please choose an email:root@domain.com
Please choose a password:root
Created user root
如果您有 Admin 类,则可以安装或更新相关的 CRUD ACL 规则
1 2 3 4 5 6 7 8
bin/console sonata:admin:setup-acl
Starting ACL AdminBundle configuration
> install ACL for sonata.media.admin.media
- add role: ROLE_SONATA_MEDIA_ADMIN_MEDIA_GUEST, permissions: ["VIEW","LIST"]
- add role: ROLE_SONATA_MEDIA_ADMIN_MEDIA_STAFF, permissions: ["EDIT","LIST","CREATE"]
- add role: ROLE_SONATA_MEDIA_ADMIN_MEDIA_EDITOR, permissions: ["OPERATOR","EXPORT"]
- add role: ROLE_SONATA_MEDIA_ADMIN_MEDIA_ADMIN, permissions: ["MASTER"]
... skipped ...
如果您已经有对象,则可以为 admin 的每个对象生成对象 ACL 规则
1
bin/console sonata:admin:generate-object-acl
(可选)您可以指定对象所有者,并逐步完成每个 admin。有关更多信息,请参阅命令的帮助。
如果您尝试访问 admin 类,您应该看到登录表单,使用 root
用户登录。
当用户具有 LIST
角色时,Admin 会显示在仪表板(和菜单)中。要更改此行为,请覆盖 Admin 类中的 showInDashboard
方法。
角色和访问控制列表
当用户使用应用程序时,可以有多个角色。每个 Admin 类都有多个角色,每个角色都指定用户对 Admin
类的权限。或更具体地说,用户可以对为 Admin
类创建的域对象执行的操作。
默认情况下,每个 Admin
类都包含以下角色,覆盖属性 $securityInformation
以更改此设置
ROLE_SONATA_..._GUEST
- 允许
VIEW
对象和LIST
对象列表的访客;
ROLE_SONATA_..._STAFF
- 可能是用户的大部分,staff 用户具有与访客相同的权限,并且还被允许
EDIT
和CREATE
新对象;
ROLE_SONATA_..._EDITOR
- 编辑器被授予所有访问权限,与 staff 用户相比,允许
DELETE
;
ROLE_SONATA_..._ADMIN
- 管理用户被授予所有访问权限,最重要的是,允许用户授予其他用户访问权限。
所有者
- 当创建对象时,当前登录的用户被设置为该对象的所有者,并被授予对该对象的所有访问权限;
- 这意味着拥有该对象的用户始终被允许
DELETE
该对象,即使他们只具有 staff 角色。
用于访问控制列表的词汇表
- 角色: 用户角色;
- ACL: 访问规则列表,Admin 使用 2 种类型;
- Admin ACL: 从每个 admin 的 Admin 类的安全信息创建,并共享访问控制条目,这些条目指定用户可以对 admin 执行的操作(权限);
对象 ACL: 也从
Admin
类的安全信息创建,但是为每个对象创建,它使用 2 个作用域- 类作用域: 类作用域包含对某个类的所有对象都有效的规则;
- 对象作用域: 指定所有者;
- Sid: 安全身份,类作用域 ACL 的 ACL 角色和对象作用域 ACL 的用户;
- Oid: 对象身份,标识 ACL,对于 admin ACL,这是 admin 代码,对于对象 ACL,这是对象 id;
- ACE: 角色(或 sid)及其权限;
- 权限: 这说明用户被允许对对象身份执行的操作;
- 位掩码: 一个权限可以有多个位掩码,每个位掩码代表一个权限。当请求
VIEW
权限并且它包含VIEW
和EDIT
位掩码,而用户仅具有EDIT
权限时,则授予VIEW
权限。 PermissionMap: 为每个权限配置位掩码,要更改默认映射,请为 Admin 的域类创建投票器。
可以有许多投票器,它们可能具有不同的权限映射。但是,请防止多个投票器使用重叠的位掩码对同一类进行投票。
有关不同权限的含义,请参阅 cookbook 文章 "`高级 ACL 概念 <https://symfony.ac.cn/doc/current/cookbook/security/acl_advanced.html#pre-authorization-decisions>`_" 。
如何授予访问权限?
在应用程序中,会询问安全上下文是否授予角色或权限的访问权限(admin.isGranted
)
- Token: token 标识请求之间的用户;
- Voter: 一种判断器,返回是否授予或拒绝访问权限,如果投票器不应针对某个案例进行投票,则返回弃权;
- AccessDecisionManager: 根据特定策略决定是否授予或拒绝访问权限。如果至少一个(肯定策略)、所有(一致策略)或超过一半(共识策略)的计数投票授予访问权限,则它会授予访问权限;
- RoleVoter: 为所有以
ROLE_
开头的属性投票,如果用户具有此角色,则授予访问权限; - RoleHierarchyVoter: 当投票角色为
ROLE_SONATA_ADMIN
(或配置中指定的角色)时,如果用户具有ROLE_SUPER_ADMIN
角色,它也会投票“授予”; - AclVoter: 如果用户具有权限,用户具有包含在要投票的权限的位掩码中的权限,或者用户拥有该对象,则授予
Admin
类的权限的访问权限。
创建自定义投票器或自定义权限映射
在某些情况下,您需要创建自定义投票器或自定义权限映射,因为例如您想使用额外的规则限制访问
创建一个自定义投票器类,该类扩展
AclVoter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
// src/Security/Authorization/Voter/UserAclVoter.php namespace App\Security\Authorization\Voter; use Sonata\UserBundle\Model\UserInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Acl\Voter\AclVoter; class UserAclVoter extends AclVoter { public function supportsClass($class) { // support the Class-Scope ACL for votes with the custom permission map // return $class === 'Sonata\UserBundle\Admin\Entity\UserAdmin' || is_subclass_of($class, 'Sonata\UserBundle\Model\UserInterface'); // if you use php >=5.3.7 you can check the inheritance with is_a($class, 'Sonata\UserBundle\Admin\Entity\UserAdmin'); // support the Object-Scope ACL return is_subclass_of($class, 'Sonata\UserBundle\Model\UserInterface'); } public function supportsAttribute($attribute) { return $attribute === 'EDIT' || $attribute === 'DELETE'; } public function vote(TokenInterface $token, $object, array $attributes) { if (!$this->supportsClass(get_class($object))) { return self::ACCESS_ABSTAIN; } foreach ($attributes as $attribute) { if ($this->supportsAttribute($attribute) && $object instanceof UserInterface) { if ($object->isSuperAdmin() && !$token->getUser()->isSuperAdmin()) { // deny a non super admin user to edit a super admin user return self::ACCESS_DENIED; } } } // use the parent vote with the custom permission map: // return parent::vote($token, $object, $attributes); // otherwise leave the permission voting to the AclVoter that is using the default permission map return self::ACCESS_ABSTAIN; } }
- (可选)创建一个自定义权限映射,复制
Sonata
到您的 bundle 以开始\AdminBundle \Security \Acl \Permission \AdminPermissionMap.php - 将投票器和权限映射声明为服务
1 2 3 4 5 6 7 8 9 10 11 12 13
<!-- config/services.xml -->
<!-- <service id="security.acl.user_permission.map" class="App\Security\Acl\Permission\UserAdminPermissionMap" public="false"></service> -->
<service id="security.acl.voter.user_permissions" class="App\Security\Authorization\Voter\UserAclVoter" public="false">
<tag name="monolog.logger" channel="security"/>
<argument type="service" id="security.acl.provider"/>
<argument type="service" id="security.acl.object_identity_retrieval_strategy"/>
<argument type="service" id="security.acl.security_identity_retrieval_strategy"/>
<argument type="service" id="security.acl.permission.map"/>
<argument type="service" id="logger" on-invalid="null"/>
<tag name="security.voter" priority="255"/>
</service>
- 将访问决策策略更改为
unanimous
1 2 3 4 5 6 7
# config/packages/security.yaml
security:
access_decision_manager:
# strategy value can be: affirmative, unanimous or consensus
strategy: unanimous
为了使其工作,需要使用对象 ACL 检查权限
- 修改适用的模板(或代码)
1 2 3 4 5 6 7 8 9 10
{% if admin.hasAccess('edit', user_object) %}
{# ... #}
{% endif %}
- because the object ACL permission is checked, the ACL for the object must
have been created, otherwise the ``AclVoter`` will deny ``EDIT`` access
for a non super admin user trying to edit another non super admin user.
This is automatically done when the object is created using the Admin.
If objects are also created outside the Admin, have a look at the
``createSecurityObject`` method in the ``AclSecurityHandler``.
用法
每次创建新的 Admin
类时,都应从命令 bin/console sonata:admin:setup-acl
开始,以便使用最新的角色和权限更新 ACL 数据库。
在模板或代码中,您可以使用 Admin 方法 hasAccess()
- 检查用户是否被允许
EDIT
的 admin
1 2 3 4 5 6 7 8 9
{# use the admin security method #}
{% if admin.hasAccess('edit') %}
{# ... #}
{% endif %}
{# or use the default is_granted Symfony helper, the following will give the same result #}
{% if is_granted('ROLE_SUPER_ADMIN') or is_granted('EDIT', admin) %}
{# ... #}
{% endif %}
- 检查用户是否被允许
DELETE
的 admin,对象也会被添加以检查对象所有者是否被允许DELETE
1 2 3 4 5 6 7 8 9
{# use the admin security method #}
{% if admin.hasAccess('delete', object) %}
{# ... #}
{% endif %}
{# or use the default is_granted Symfony helper, the following will give the same result #}
{% if is_granted('ROLE_SUPER_ADMIN') or is_granted('DELETE', object) %}
{# ... #}
{% endif %}
列表过滤
使用 ACL 的列表过滤作为第三方 bundle 提供:CoopTilleulsAclSonataAdminExtensionBundle。启用后,登录用户将仅看到他们具有 VIEW
权限(或更高级别)的对象。
ACL 编辑器
SonataAdminBundle 提供了一个用户友好的 ACL 编辑器界面。如果使用 sonata.admin.security.handler.acl
安全处理程序并正确配置,它将自动可用。
ACL 编辑器仅适用于对对象实例具有 OWNER
或 MASTER
权限的用户。OWNER
和 MASTER
权限只能由对对象实例具有 OWNER
权限的用户编辑。

用户列表自定义
默认情况下,ACL 编辑器允许为 SonataUserBundle
管理的所有用户设置权限。
要自定义显示的用户,请覆盖 Sonata\AdminBundle\Controller\CRUDController::getAclUsers()
。此方法必须返回用户的可迭代集合
1 2 3 4 5 6 7 8 9 10
protected function getAclUsers(): \Traversable
{
$userManager = $container->get('sonata.user.manager.user');
// Display only kevin and anne
$users[] = $userManager->findUserByUsername('kevin');
$users[] = $userManager->findUserByUsername('anne');
return new \ArrayIterator($users);
}
角色列表自定义
默认情况下,ACL 编辑器允许为所有角色设置权限。
要自定义显示的角色,请覆盖 Sonata\AdminBundle\Controller\CRUDController::getAclRoles()
。此方法必须返回角色的可迭代集合
1 2 3 4 5 6 7 8 9 10
protected function getAclRoles(): \Traversable
{
// Display only ROLE_BAPTISTE and ROLE_HELENE
$roles = [
'ROLE_BAPTISTE',
'ROLE_HELENE'
];
return new \ArrayIterator($roles);
}