我正在使用帶有 JWT 令牌(通過 LexikJWTAuthenticationBundle)的 API 平臺的 PHP symfony,截至今天的最新版本。
我已經閱讀了很多東西,并且我知道如何做基本的事情:
- 創建一個公開我的物體的 API,
- 使用 JWT 保護某些端點
- 使用 user_roles 保護某些端點
我現在要做的是讓 API 只發回屬于用戶的資料,而不是簡單地發回資料庫中包含并由物體表示的所有內容。我的作業以此為基礎,但這沒有考慮 JWT 令牌,我不知道如何在 UserFilter 類中使用令牌:https ://api-platform.com/docs/core/filters/ #using-doctrine-orm-filters
這是我的書物體:
<?php
// api/src/Entity/Book.php
namespace App\Entity;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\Put;
use ApiPlatform\Metadata\Patch;
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\GetCollection;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use App\Entity\User;
use App\Attribute\UserAware;
/** A book. */
#[ORM\Entity]
#[ApiResource(operations: [
new Get(),
new GetCollection(),
new Post(),
new Put(),
new Patch(),
new Delete()
])]
#[UserAware(userFieldName: "id")]
class Book
{
/** The id of this book. */
#[ORM\Id, ORM\Column, ORM\GeneratedValue]
private ?int $id = null;
/** The ISBN of this book (or null if doesn't have one). */
#[ORM\Column(nullable: true)]
#[Assert\Isbn]
public ?string $isbn = null;
/** The title of this book. */
#[ORM\Column]
#[Assert\NotBlank]
public string $title = '';
/** The description of this book. */
#[ORM\Column(type: 'text')]
#[Assert\NotBlank]
public string $description = '';
/** The author of this book. */
#[ORM\Column]
#[Assert\NotBlank]
public string $author = '';
/** The publication date of this book. */
#[ORM\Column(type: 'datetime')]
#[Assert\NotNull]
public ?\DateTime $publicationDate = null;
/** @var Review[] Available reviews for this book. */
#[ORM\OneToMany(targetEntity: Review::class, mappedBy: 'book', cascade: ['persist', 'remove'])]
public iterable $reviews;
#[ORM\Column(length: 255, nullable: true)]
private ?string $publisher = null;
/** The book this user is about. */
#[ORM\ManyToOne(inversedBy: 'books')]
#[ORM\JoinColumn(name: 'user_id', referencedColumnName: 'id')]
#[Assert\NotNull]
public ?User $user = null;
public function __construct()
{
$this->reviews = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getPublisher(): ?string
{
return $this->publisher;
}
public function setPublisher(?string $publisher): self
{
$this->publisher = $publisher;
return $this;
}
}
這是我的 UserFilter 類:
<?php
// api/src/Filter/UserFilter.php
namespace App\Filter;
use App\Attribute\UserAware;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Query\Filter\SQLFilter;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use App\Entity\User;
final class UserFilter extends SQLFilter
{
public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias): string
{
// The Doctrine filter is called for any query on any entity
// Check if the current entity is "user aware" (marked with an attribute)
$userAware = $targetEntity->getReflectionClass()->getAttributes(UserAware::class)[0] ?? null;
$fieldName = $userAware?->getArguments()['userFieldName'] ?? null;
if ($fieldName === '' || is_null($fieldName)) {
return '';
}
try {
$userId = $this->getParameter('id');
// Don't worry, getParameter automatically escapes parameters
} catch (\InvalidArgumentException $e) {
// No user id has been defined
return '';
}
if (empty($fieldName) || empty($userId)) {
return '';
}
return sprintf('%s.%s = %s', $targetTableAlias, $fieldName, $userId);
}
}
這是我的 UserAware 類:
<?php
// api/Annotation/UserAware.php
namespace App\Attribute;
use Attribute;
#[Attribute(Attribute::TARGET_CLASS)]
final class UserAware
{
public $userFieldName;
}
我將此添加到我的 config/packages/api_platform.yaml 檔案中:
doctrine:
orm:
filters:
user_filter:
class: App\Filter\UserFilter
enabled: true
它顯然不起作用,因為我沒有在 JWT 令牌和過濾器之間架起一座橋梁,但我不知道該怎么做。我錯過了什么?我目前的結果是 GET /api/books 發回了存盤在資料庫中的所有書籍,而不是只發送屬于 JWT 身份驗證用戶的書籍。
uj5u.com熱心網友回復:
您可以使用 Doctrine Extension 代替 Doctrine Filter,如此處所述。在您的情況下,它需要:
- 創建學說擴展:
<?php
// api/src/Doctrine/CurrentUserExtension.php
namespace App\Doctrine;
use ApiPlatform\Doctrine\Orm\Extension\QueryCollectionExtensionInterface;
use ApiPlatform\Doctrine\Orm\Extension\QueryItemExtensionInterface;
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use ApiPlatform\Metadata\Operation;
use App\Entity\Book;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Security\Core\Security;
final class CurrentUserExtension implements QueryCollectionExtensionInterface, QueryItemExtensionInterface
{
private $security;
public function __construct(Security $security)
{
$this->security = $security;
}
public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, Operation $operation = null, array $context = []): void
{
$this->addWhere($queryBuilder, $resourceClass);
}
public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, Operation $operation = null, array $context = []): void
{
$this->addWhere($queryBuilder, $resourceClass);
}
private function addWhere(QueryBuilder $queryBuilder, string $resourceClass): void
{
if (Book::class !== $resourceClass || $this->security->isGranted('ROLE_ADMIN') || null === $user = $this->security->getUser()) {
return;
}
$rootAlias = $queryBuilder->getRootAliases()[0];
$queryBuilder->andWhere(sprintf('%s.user = :current_user', $rootAlias));
$queryBuilder->setParameter('current_user', $user->getId());
}
}
主要邏輯在addWhere()方法中:
- 僅在您處理 Book 物體時適用(但您可以將想法擴展到此處的物體串列)
- 檢查用戶是否被授予管理員權限(如果是這樣,它會跳過擴展,允許管理員獲取所有書籍)
- 如果用戶未通過身份驗證,則跳過(您應該使用端點中的防火墻或安全性來防止這種訪問)
然后它向 SQL 查詢添加 where 條件以按 userId(或您需要的任何其他條件)進行過濾
- 不要忘記啟用您的過濾器:
# api/config/services.yaml
services:
# ...
'App\Doctrine\CurrentUserExtension':
tags:
- { name: api_platform.doctrine.orm.query_extension.collection }
- { name: api_platform.doctrine.orm.query_extension.item }
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/521211.html
上一篇:@ParamConverter注釋找不到App\\Entity\\Project物件
下一篇:無法從容器中獲取控制器,因為它是私有的。您是否忘記使用“controller.service_arguments”標記服務?
