Php Symfony-投票者在/GET列表上返回空对象
我正在使用应用程序上实现的Php Symfony-投票者在/GET列表上返回空对象,php,symfony,jwt,api-platform.com,symfony4-voter,Php,Symfony,Jwt,Api Platform.com,Symfony4 Voter,我正在使用应用程序上实现的Symfony5和apiplate 我有一个经典的/GET路由,它通过上下文生成器向未经身份验证的用户的某些属性添加动态组“匿名:读取”,从而只返回所有用户实体的少数字段 由于用户不需要拥有JWT令牌,我已将其添加到我的security.yml文件的access\u control部分: access_control: - { path: ^/users, roles: IS_AUTHENTICATED_ANONYMOUSLY } 此路由也被具有不同授权的其他类型
Symfony5
和apiplate
我有一个经典的/GET
路由,它通过上下文生成器向未经身份验证的用户的某些属性添加动态组“匿名:读取”
,从而只返回所有用户
实体的少数字段
由于用户不需要拥有JWT令牌
,我已将其添加到我的security.yml
文件的access\u control
部分:
access_control:
- { path: ^/users, roles: IS_AUTHENTICATED_ANONYMOUSLY }
此路由也被具有不同授权的其他类型的用户使用,因此它通过一个投票者
而投票者
是我阻止的地方,我应该在$subject
变量中接收的对象列表最终为空,从投票者的支持
函数返回false,从而拒绝访问
如果我从/GET
路径注释中撤销安全性
约束,我确实会收到我想要的用户列表,其中只包含所需的字段
我无法理解为什么此对象列表的$subject
为空
User.php:
/**
* @ApiResource(
* attributes={
* "normalization_context"={"groups"={"user:read", "user:list"}},
* "denormalization_context"={"groups"={"user:write"}},
* "order"={"availabilities.start": "ASC"}
* },
* collectionOperations={
* "get"={
* "mehtod"="GET",
* "security"="is_granted('LIST', object)",
* "normalization_context"={"groups"={"user:list"}},
* },
* "post"={
* "method"="POST",
* "security_post_denormalize"="is_granted('POST', object)",
* "denormalization_context"={"groups"={"user:write"}}
* }
* },
* itemOperations={
* "get"={
* "method"="GET",
* "security"="is_granted('ROLE_USER')"
* },
* "put"={
* "method"="PUT",
* "security"="is_granted('PUT', object)"
* },
* "delete"={
* "method"="DELETE",
* "security"="is_granted('ROLE_ADMIN')"
* }
* }
* )
* @ORM\Entity(repositoryClass=UserRepository::class)
*/
class User
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
* @Groups({"user:read", "user:list"})
*/
private $id;
/**
* @ORM\Column(type="boolean")
* @Groups({"user:read", "user:list", "user:write", "anonym:read"})
*/
private $active;
/**
* @ORM\Column(type="string", length=255, nullable=true)
* @Groups({"user:read", "user:list", "user:write", "anonym:read"})
*/
private $photo;
/**
* @ORM\Column(type="text")
* @Groups({"user:read", "user:list", "user:write"})
*/
private $description;
}
final class UserContextBuilder 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);
$resourceClass = $context['resource_class'] ?? null;
if (
$resourceClass === User::class && isset($context['groups']) &&
!$this->authorizationChecker->isGranted('ROLE_USER') &&
true == $normalization
) {
$context['groups'][] = 'anonym:read';
}
return $context;
}
}
class UserVoter extends AbstractVoter
{
protected function supports($attribute, $subject)
{
return parent::supports($attribute, $subject) &&
($subject instanceof User ||
$this->arrayOf($subject, User::class));
}
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
{
/** @var User $user */
$user = $token->getUser();
if ($this->accessDecisionManager->decide($token, [GenericRoles::ROLE_ADMIN])) {
return true;
}
switch ($attribute) {
case Actions::LIST:
return $user->hasRole(GenericRoles::ROLE_USER) || !$user instanceof User;
case Actions::PUT:
return ($user->getId() == $subject->getUser()->getId() ||
$user->hasRole(GenericRoles::SOME_SPECIAL_ROLE));
case Actions::POST:
return $user->hasRole(GenericRoles::SOME_SPECIAL_ROLE);
}
return false;
}
}
UserContextBuilder.php:
/**
* @ApiResource(
* attributes={
* "normalization_context"={"groups"={"user:read", "user:list"}},
* "denormalization_context"={"groups"={"user:write"}},
* "order"={"availabilities.start": "ASC"}
* },
* collectionOperations={
* "get"={
* "mehtod"="GET",
* "security"="is_granted('LIST', object)",
* "normalization_context"={"groups"={"user:list"}},
* },
* "post"={
* "method"="POST",
* "security_post_denormalize"="is_granted('POST', object)",
* "denormalization_context"={"groups"={"user:write"}}
* }
* },
* itemOperations={
* "get"={
* "method"="GET",
* "security"="is_granted('ROLE_USER')"
* },
* "put"={
* "method"="PUT",
* "security"="is_granted('PUT', object)"
* },
* "delete"={
* "method"="DELETE",
* "security"="is_granted('ROLE_ADMIN')"
* }
* }
* )
* @ORM\Entity(repositoryClass=UserRepository::class)
*/
class User
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
* @Groups({"user:read", "user:list"})
*/
private $id;
/**
* @ORM\Column(type="boolean")
* @Groups({"user:read", "user:list", "user:write", "anonym:read"})
*/
private $active;
/**
* @ORM\Column(type="string", length=255, nullable=true)
* @Groups({"user:read", "user:list", "user:write", "anonym:read"})
*/
private $photo;
/**
* @ORM\Column(type="text")
* @Groups({"user:read", "user:list", "user:write"})
*/
private $description;
}
final class UserContextBuilder 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);
$resourceClass = $context['resource_class'] ?? null;
if (
$resourceClass === User::class && isset($context['groups']) &&
!$this->authorizationChecker->isGranted('ROLE_USER') &&
true == $normalization
) {
$context['groups'][] = 'anonym:read';
}
return $context;
}
}
class UserVoter extends AbstractVoter
{
protected function supports($attribute, $subject)
{
return parent::supports($attribute, $subject) &&
($subject instanceof User ||
$this->arrayOf($subject, User::class));
}
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
{
/** @var User $user */
$user = $token->getUser();
if ($this->accessDecisionManager->decide($token, [GenericRoles::ROLE_ADMIN])) {
return true;
}
switch ($attribute) {
case Actions::LIST:
return $user->hasRole(GenericRoles::ROLE_USER) || !$user instanceof User;
case Actions::PUT:
return ($user->getId() == $subject->getUser()->getId() ||
$user->hasRole(GenericRoles::SOME_SPECIAL_ROLE));
case Actions::POST:
return $user->hasRole(GenericRoles::SOME_SPECIAL_ROLE);
}
return false;
}
}
UserVoter.php:
/**
* @ApiResource(
* attributes={
* "normalization_context"={"groups"={"user:read", "user:list"}},
* "denormalization_context"={"groups"={"user:write"}},
* "order"={"availabilities.start": "ASC"}
* },
* collectionOperations={
* "get"={
* "mehtod"="GET",
* "security"="is_granted('LIST', object)",
* "normalization_context"={"groups"={"user:list"}},
* },
* "post"={
* "method"="POST",
* "security_post_denormalize"="is_granted('POST', object)",
* "denormalization_context"={"groups"={"user:write"}}
* }
* },
* itemOperations={
* "get"={
* "method"="GET",
* "security"="is_granted('ROLE_USER')"
* },
* "put"={
* "method"="PUT",
* "security"="is_granted('PUT', object)"
* },
* "delete"={
* "method"="DELETE",
* "security"="is_granted('ROLE_ADMIN')"
* }
* }
* )
* @ORM\Entity(repositoryClass=UserRepository::class)
*/
class User
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
* @Groups({"user:read", "user:list"})
*/
private $id;
/**
* @ORM\Column(type="boolean")
* @Groups({"user:read", "user:list", "user:write", "anonym:read"})
*/
private $active;
/**
* @ORM\Column(type="string", length=255, nullable=true)
* @Groups({"user:read", "user:list", "user:write", "anonym:read"})
*/
private $photo;
/**
* @ORM\Column(type="text")
* @Groups({"user:read", "user:list", "user:write"})
*/
private $description;
}
final class UserContextBuilder 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);
$resourceClass = $context['resource_class'] ?? null;
if (
$resourceClass === User::class && isset($context['groups']) &&
!$this->authorizationChecker->isGranted('ROLE_USER') &&
true == $normalization
) {
$context['groups'][] = 'anonym:read';
}
return $context;
}
}
class UserVoter extends AbstractVoter
{
protected function supports($attribute, $subject)
{
return parent::supports($attribute, $subject) &&
($subject instanceof User ||
$this->arrayOf($subject, User::class));
}
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
{
/** @var User $user */
$user = $token->getUser();
if ($this->accessDecisionManager->decide($token, [GenericRoles::ROLE_ADMIN])) {
return true;
}
switch ($attribute) {
case Actions::LIST:
return $user->hasRole(GenericRoles::ROLE_USER) || !$user instanceof User;
case Actions::PUT:
return ($user->getId() == $subject->getUser()->getId() ||
$user->hasRole(GenericRoles::SOME_SPECIAL_ROLE));
case Actions::POST:
return $user->hasRole(GenericRoles::SOME_SPECIAL_ROLE);
}
return false;
}
}
如果有人知道为什么会这样,我会非常感激