我目前正在使用FOSRESTBundle和JMSSerialize制作RESTFull API(当然)。
我的项目是客户和管理员的外联网。
通过这种方式,我必须禁止客户查看某个字段,仅对管理员可见。
我开始为实体制作此序列化程序配置:
AppBundle\Entity\IncidentComment:
exclusion_policy: ALL
properties:
id:
expose: true
groups: [list, details]
author:
expose: true
groups: [list, details]
addedAt:
expose: true
groups: [list, details]
content:
expose: true
groups: [details]
customerVisible:
expose: true
groups: [list_admin, details_admin]
如您所见,customerVisible
组后缀为_admin
。此字段仅应显示给管理员。
我想动态添加带有_admin
后缀的组来设置视图上的组,如果用户有例如ROLE_ADMIN角色或其他条件而没有在每个其他控制器的每个操作上写入它。
我正在考虑使用安全上下文参数创建一个custom view handler来添加组,但我不知道是否是正确的方法。
你认为这是好方法吗?你有什么建议吗? 顺便说一下,如果某个开发者遇到同样的问题,我会很高兴他在这里解决了这个问题! :)感谢。
答案 0 :(得分:3)
我刚刚想出了一种在运行时添加SerializerGroups的简单方法:
private function determineRolebasedSerializerGroup($role, $groupToAdd, Request $request) {
if (!$this->get('security.context')->isGranted($role))
return;
$groups = $request->attributes->get('_view')->getSerializerGroups();
$groups[] = $groupToAdd;
$x = $request->attributes->get('_view')->setSerializerGroups($groups);
}
我已将此方法添加到我的控制器中。我现在能够这样称呼它:
/**
* @REST\View(serializerGroups={"company"})
*/
public function getCompanyAction(Company $company, Request $request) {
$this->determineRolebasedSerializerGroup('ROLE_ADMIN', 'company-admin', $request);
return $company;
}
如果当前用户具有角色“ROLE_ADMIN”,则会将“company-admin”组添加到序列化程序组。这对我来说非常有用。
答案 1 :(得分:2)
如果您想使用侦听器执行此操作,则可以创建自己的ViewResponseListener
并将其订阅到事件kernel.view
。
您的侦听器必须在FOSRest侦听器之后触发,因此,您必须设置101优先级。
app.event.listener.extended_view_response:
class: AppBundle\EventListener\ExtendedViewResponseListener
arguments: ["@security.authorization_checker"]
tags:
- { name: kernel.event_listener, event: kernel.view, method: onKernelView, priority: 101 }
ExtendedViewResponseListener.php
public function onKernelView(GetResponseForControllerResultEvent $event)
{
if (null !== $viewAttribute = $event->getRequest()->attributes->get('_template')) {
$groups = [];
foreach(User::getPossibleRoles() as $role) {
if ($this->authorizationChecker->isGranted($role)) {
$groups[] = strtolower(str_replace('ROLE_', '', $role)); // ROLE_USER => user group
}
}
$viewAttribute->setSerializerGroups($groups);
}
}
而且,请不要忘记在控制器中启用_template
属性,我的意思是
控制器注释中的@\FOS\RestBundle\Controller\Annotations\View()
。
如果你想弄清楚它是如何工作的 - 请检查FosRestBundle中的ViewResponseListener.php。
第二种方法 - 您的自定义序列化程序tokenstorageaware_serializer
答案 2 :(得分:1)
除了@Vladislav Kopaygorodsky的回复之外,即使您从控制器中的函数中省略@View
注释,这些添加也允许它工作:
namespace AppBundle\EventListener;
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
use AppBundle\Security\User;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use FOS\RestBundle\Controller\Annotations\View as ViewAnnotation;
/**
* Listener to automatically adjust serializer groups based on user roles.
*
* If a user is granted the USER_XYZ role, then this function will add the
* serializer group "has_role_xyz" before the automatic serialization takes
* place on the data returned from a controller.
*/
class PermissionResponseListener
{
private $authorizationChecker;
public function __construct(AuthorizationCheckerInterface $authorizationChecker)
{
$this->authorizationChecker = $authorizationChecker;
}
public function onKernelView(GetResponseForControllerResultEvent $event)
{
$attr = $event->getRequest()->attributes;
if (null === $viewAttribute = $attr->get('_template')) {
// No @Rest\View annotation, create a blank one.
$viewAttribute = new ViewAnnotation(array());
$viewAttribute->setPopulateDefaultVars(false);
$attr->set('_template', $viewAttribute);
}
$groups = $viewAttribute->getSerializerGroups();
// Always include this group, since the default value set in
// config.yml is no longer used.
$groups[] = 'Default';
foreach (User::getPossibleRoles() as $role) {
if ($this->authorizationChecker->isGranted($role)) {
$groups[] = 'has_' . strtolower($role); // ROLE_USER => has_role_user
}
}
$viewAttribute->setSerializerGroups($groups);
}
}
User类有一个只列出所有可用角色的函数:
public static function getPossibleRoles()
{
return [
'ROLE_ADMIN', // system administrators
'ROLE_OFFICE', // data entry staff
'ROLE_USER', // anyone logged in
];
}
和services.yml:
# Set serializer groups based on user roles
AppBundle\EventListener\PermissionResponseListener:
public: false
tags:
- { name: kernel.event_listener, event: kernel.view, method: onKernelView, priority: 101 }
在实体中,您现在可以使用注释:
class ExampleEntity {
/**
* @Serializer\Groups({"has_role_admin"})
*/
protected $adminOnlyValue;
答案 3 :(得分:0)
补充上面 Vladislav Kopaygorodsky 的回答并回答 Malvineous 提出的问题:
根据Symfony (5.2) documentation,事件监听者/订阅者优先级是一个整数。值越高,运行越早。在撰写本文时,FOS Rest 包的 ViewResponseListener 将自身设置为 30。因此,如果自定义侦听器/订阅者的优先级为 101,它将在 FOS 优先级之前运行。