如何根据用户角色和请求方式设置归一化组
How to set normalization groups based on user's role and request method
我正在创建沙盒应用作为 Api 平台实践,我有以下问题需要解决:
让我们考虑以下用户实体的 REST 端点:
免责声明 在代码示例中有更多的属性,但整个概念适用于
Collection-get(又名。/api/users) - 仅适用于管理员用户(所有属性均可用,也许我们排除了散列密码)
POST - 每个人都应该有权访问以下属性:用户名、电子邮件、明文密码(不会保留以防万一有人问)
PATCH/PUT - 这里变得非常棘手:我希望那些 ROLE_ADMIN 的人能够访问用户名、电子邮件、纯密码字段。而那些所有者只能更改 plainPassword
DELETE - 只有 ROLE_ADMIN 和所有者可以删除
我将从资源配置开始
resources:
App\Entity\User:
# attributes:
# normalization_context:
# groups: ['read', 'put', 'patch', 'post', 'get', 'collection:get']
# denormalization_context:
# groups: ['read', 'put', 'patch', 'post', 'get', 'collection:get']
collectionOperations:
get:
security: 'is_granted("ROLE_ADMIN")'
normalization_context: { groups: ['collection:get'] }
post:
normalization_context: { groups: ['admin:post', 'post'] }
itemOperations:
get:
normalization_context: { groups: ['admin:get', 'get'] }
security: 'is_granted("ROLE_ADMIN") or object == user'
put:
normalization_context: { groups: ['admin:put', 'put'] }
security: 'is_granted("ROLE_ADMIN") or object == user'
patch:
normalization_context: { groups: ['admin:patch', 'patch'] }
security: 'is_granted("ROLE_ADMIN") or object == user'
delete:
security: 'is_granted("ROLE_ADMIN") or object == user'
这是序列化器配置
App\Entity\User:
attributes:
username:
groups: ['post', 'admin:put', 'admin:patch', 'collection:get', 'get']
email:
groups: ['post', 'admin:put', 'admin:patch', 'collection:get', 'get']
firstName:
groups: ['post', 'admin:put', 'admin:patch', 'collection:get', 'get']
lastName:
groups: ['post', 'admin:put', 'admin:patch', 'collection:get', 'get']
plainPassword:
groups: ['post', patch]
createdAt:
groups: ['get', 'collection:get']
lastLoginDate:
groups: ['get', 'collection:get']
updatedAt:
groups: ['collection:get']
这是上下文组生成器(已注册为服务,如 API-平台文档
中所述
<?php
namespace App\Serializer;
use Symfony\Component\HttpFoundation\Request;
use ApiPlatform\Core\Serializer\SerializerContextBuilderInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
final class AdminContextBuilder implements SerializerContextBuilderInterface
{
private $decorated;
private $authorizationChecker;
public function __construct(SerializerContextBuilderInterface $decorated, AuthorizationCheckerInterface $authorizationChecker)
{
$this->decorated = $decorated;
$this->authorizationChecker = $authorizationChecker;
}
public function createFromRequest(Request $request, bool $normalization, ?array $extractedAttributes = null): array
{
$context = $this->decorated->createFromRequest($request, $normalization, $extractedAttributes);
if ($this->authorizationChecker->isGranted('ROLE_ADMIN')) {
switch($request->getMethod()) {
case 'GET':
$context['groups'][] = 'admin:get';
break;
case 'POST':
$context['groups'][] = 'admin:post';
case 'PUT':
$context['groups'][] = 'admin:put';
case 'PATCH':
$context['groups'][] = 'admin:patch';
}
}
return $context;
}
}
问题是,即使我以只有 ROLE_USER 的用户身份登录,我仍然能够更改根据 admin:patch 规范化组应锁定的用户名字段。我是 api 平台的新手,我不太明白为什么这不起作用,但我猜上下文构建器会有问题。感谢您的帮助,如果我同时想出一些办法,我会及时更新问题
在调查了文档和浏览了 youtube 之后,最重要的是对上述用户资源进行了试验,我想出了解决方案
让我们再次从配置开始:
resources:
App\Entity\User:
collectionOperations:
get:
security: 'is_granted("ROLE_ADMIN")'
normalization_context: { groups: ['collection:get'] }
denormalization_context: { groups: ['collection:get'] }
post:
normalization_context: { groups: ['post'] }
denormalization_context: { groups: ['post'] }
itemOperations:
get:
normalization_context: { groups: ['get'] }
security: 'is_granted("ROLE_ADMIN") or object == user'
patch:
normalization_context: { groups: ['patch'] }
denormalization_context: { groups: ['patch'] }
security: 'is_granted("ROLE_ADMIN") or object == user'
delete:
security: 'is_granted("ROLE_ADMIN") or object == user'
起点之间的主要区别是管理员操作永远不应在操作组中声明,因为它们将默认添加到上下文中。
接下来是 属性 组,我们在其中定义某些 属性
上可用的所有操作
App\Entity\User:
attributes:
id:
groups: ['get', 'collection:get']
username:
groups: ['post', 'admin:patch', 'get', 'collection:get']
email:
groups: ['post', 'admin:patch', 'get', 'collection:get']
plainPassword:
groups: ['post', 'patch', 'collection:get']
firstName:
groups: ['post', 'patch', 'get', 'collection:get']
lastName:
groups: ['post', 'get', 'collection:get']
createdAt:
groups: ['get', 'collection:get']
lastLoginDate:
groups: ['get', 'collection:get']
updatedAt:
groups: ['collection:get']
这与问题中的问题完全相同,我们唯一需要配置的是哪些操作需要 'admin' 无论您对博客、图书馆、商店进行编程,都可以根据您的需要进行更改或其他任何东西,并且需要在 API.
上为每个角色执行一些自定义操作
最后是自定义上下文构建器
<?php
namespace App\Serializer;
use Symfony\Component\HttpFoundation\Request;
use ApiPlatform\Core\Serializer\SerializerContextBuilderInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
final class AdminContextBuilder implements SerializerContextBuilderInterface
{
private $decorated;
private $authorizationChecker;
public function __construct(SerializerContextBuilderInterface $decorated, AuthorizationCheckerInterface $authorizationChecker)
{
$this->decorated = $decorated;
$this->authorizationChecker = $authorizationChecker;
}
public function createFromRequest(Request $request, bool $normalization, ?array $extractedAttributes = null): array
{
$context = $this->decorated->createFromRequest($request, $normalization, $extractedAttributes);
if ($this->authorizationChecker->isGranted('ROLE_ADMIN')) {
$context['groups'][] = 'admin:patch';
$context['groups'][] = 'admin:post';
$context['groups'][] = 'admin:get';
}
return $context;
}
}
这很简单,可以根据您的个人需求进行扩展,基本上我们会检查当前用户是否是管理员,并为他提供组 a、b、c 等的属性。这也可以按实体指定(更多信息您可以在 API 平台文档中找到 BookContextBuilder 非常简单
我很确定这是任何人在构建一些简单甚至复杂的 API 时都需要的面包和黄油,其中角色将决定谁可以做什么。如果这个答案对您有帮助,请务必补充我的答案,非常感谢,祝您编码愉快!
我正在创建沙盒应用作为 Api 平台实践,我有以下问题需要解决:
让我们考虑以下用户实体的 REST 端点:
免责声明 在代码示例中有更多的属性,但整个概念适用于
Collection-get(又名。/api/users) - 仅适用于管理员用户(所有属性均可用,也许我们排除了散列密码)
POST - 每个人都应该有权访问以下属性:用户名、电子邮件、明文密码(不会保留以防万一有人问)
PATCH/PUT - 这里变得非常棘手:我希望那些 ROLE_ADMIN 的人能够访问用户名、电子邮件、纯密码字段。而那些所有者只能更改 plainPassword
DELETE - 只有 ROLE_ADMIN 和所有者可以删除
我将从资源配置开始
resources:
App\Entity\User:
# attributes:
# normalization_context:
# groups: ['read', 'put', 'patch', 'post', 'get', 'collection:get']
# denormalization_context:
# groups: ['read', 'put', 'patch', 'post', 'get', 'collection:get']
collectionOperations:
get:
security: 'is_granted("ROLE_ADMIN")'
normalization_context: { groups: ['collection:get'] }
post:
normalization_context: { groups: ['admin:post', 'post'] }
itemOperations:
get:
normalization_context: { groups: ['admin:get', 'get'] }
security: 'is_granted("ROLE_ADMIN") or object == user'
put:
normalization_context: { groups: ['admin:put', 'put'] }
security: 'is_granted("ROLE_ADMIN") or object == user'
patch:
normalization_context: { groups: ['admin:patch', 'patch'] }
security: 'is_granted("ROLE_ADMIN") or object == user'
delete:
security: 'is_granted("ROLE_ADMIN") or object == user'
这是序列化器配置
App\Entity\User:
attributes:
username:
groups: ['post', 'admin:put', 'admin:patch', 'collection:get', 'get']
email:
groups: ['post', 'admin:put', 'admin:patch', 'collection:get', 'get']
firstName:
groups: ['post', 'admin:put', 'admin:patch', 'collection:get', 'get']
lastName:
groups: ['post', 'admin:put', 'admin:patch', 'collection:get', 'get']
plainPassword:
groups: ['post', patch]
createdAt:
groups: ['get', 'collection:get']
lastLoginDate:
groups: ['get', 'collection:get']
updatedAt:
groups: ['collection:get']
这是上下文组生成器(已注册为服务,如 API-平台文档
中所述<?php
namespace App\Serializer;
use Symfony\Component\HttpFoundation\Request;
use ApiPlatform\Core\Serializer\SerializerContextBuilderInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
final class AdminContextBuilder implements SerializerContextBuilderInterface
{
private $decorated;
private $authorizationChecker;
public function __construct(SerializerContextBuilderInterface $decorated, AuthorizationCheckerInterface $authorizationChecker)
{
$this->decorated = $decorated;
$this->authorizationChecker = $authorizationChecker;
}
public function createFromRequest(Request $request, bool $normalization, ?array $extractedAttributes = null): array
{
$context = $this->decorated->createFromRequest($request, $normalization, $extractedAttributes);
if ($this->authorizationChecker->isGranted('ROLE_ADMIN')) {
switch($request->getMethod()) {
case 'GET':
$context['groups'][] = 'admin:get';
break;
case 'POST':
$context['groups'][] = 'admin:post';
case 'PUT':
$context['groups'][] = 'admin:put';
case 'PATCH':
$context['groups'][] = 'admin:patch';
}
}
return $context;
}
}
问题是,即使我以只有 ROLE_USER 的用户身份登录,我仍然能够更改根据 admin:patch 规范化组应锁定的用户名字段。我是 api 平台的新手,我不太明白为什么这不起作用,但我猜上下文构建器会有问题。感谢您的帮助,如果我同时想出一些办法,我会及时更新问题
在调查了文档和浏览了 youtube 之后,最重要的是对上述用户资源进行了试验,我想出了解决方案
让我们再次从配置开始:
resources:
App\Entity\User:
collectionOperations:
get:
security: 'is_granted("ROLE_ADMIN")'
normalization_context: { groups: ['collection:get'] }
denormalization_context: { groups: ['collection:get'] }
post:
normalization_context: { groups: ['post'] }
denormalization_context: { groups: ['post'] }
itemOperations:
get:
normalization_context: { groups: ['get'] }
security: 'is_granted("ROLE_ADMIN") or object == user'
patch:
normalization_context: { groups: ['patch'] }
denormalization_context: { groups: ['patch'] }
security: 'is_granted("ROLE_ADMIN") or object == user'
delete:
security: 'is_granted("ROLE_ADMIN") or object == user'
起点之间的主要区别是管理员操作永远不应在操作组中声明,因为它们将默认添加到上下文中。
接下来是 属性 组,我们在其中定义某些 属性
App\Entity\User:
attributes:
id:
groups: ['get', 'collection:get']
username:
groups: ['post', 'admin:patch', 'get', 'collection:get']
email:
groups: ['post', 'admin:patch', 'get', 'collection:get']
plainPassword:
groups: ['post', 'patch', 'collection:get']
firstName:
groups: ['post', 'patch', 'get', 'collection:get']
lastName:
groups: ['post', 'get', 'collection:get']
createdAt:
groups: ['get', 'collection:get']
lastLoginDate:
groups: ['get', 'collection:get']
updatedAt:
groups: ['collection:get']
这与问题中的问题完全相同,我们唯一需要配置的是哪些操作需要 'admin' 无论您对博客、图书馆、商店进行编程,都可以根据您的需要进行更改或其他任何东西,并且需要在 API.
上为每个角色执行一些自定义操作
最后是自定义上下文构建器
<?php
namespace App\Serializer;
use Symfony\Component\HttpFoundation\Request;
use ApiPlatform\Core\Serializer\SerializerContextBuilderInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
final class AdminContextBuilder implements SerializerContextBuilderInterface
{
private $decorated;
private $authorizationChecker;
public function __construct(SerializerContextBuilderInterface $decorated, AuthorizationCheckerInterface $authorizationChecker)
{
$this->decorated = $decorated;
$this->authorizationChecker = $authorizationChecker;
}
public function createFromRequest(Request $request, bool $normalization, ?array $extractedAttributes = null): array
{
$context = $this->decorated->createFromRequest($request, $normalization, $extractedAttributes);
if ($this->authorizationChecker->isGranted('ROLE_ADMIN')) {
$context['groups'][] = 'admin:patch';
$context['groups'][] = 'admin:post';
$context['groups'][] = 'admin:get';
}
return $context;
}
}
这很简单,可以根据您的个人需求进行扩展,基本上我们会检查当前用户是否是管理员,并为他提供组 a、b、c 等的属性。这也可以按实体指定(更多信息您可以在 API 平台文档中找到 BookContextBuilder 非常简单
我很确定这是任何人在构建一些简单甚至复杂的 API 时都需要的面包和黄油,其中角色将决定谁可以做什么。如果这个答案对您有帮助,请务必补充我的答案,非常感谢,祝您编码愉快!