php - Symfony 4 投票者注释(@IsGranted)
问题描述
我正在尝试使用 Symfony Voters 和 Controller Annotation 来允许或限制对 Symfony 4 应用程序中某些操作的访问。
例如,我的前端提供删除“帖子”的功能,但前提是用户为该帖子设置了“DELETE_POST”属性。
前端向我的 symfony 端点发送一个 HTTP“DELETE”操作,在 URL 中传递帖子的 id(即/api/post/delete/19
)。
我正在尝试使用@IsGranted
注释,如此处所述。
这是我的 symfony 端点:
/**
* @Route("/delete/{id}")
* @Method("DELETE")
* @IsGranted("DELETE_POST", subject="post")
*/
public function deletePost($post) {
... some logic to delete post
return new Response("Deleting " . $post->getId());
}
这是我的选民:
class PostVoter extends Voter {
private $attributes = array(
"VIEW_POST", "EDIT_POST", "DELETE_POST", "CREATE_POST"
);
protected function supports($attribute, $subject) {
return in_array($attribute, $this->attributes, true) && $subject instanceof Post;
}
protected function voteOnAttribute($attribute, $subject, TokenInterface $token) {
... logic to figure out if user has permissions.
return $check;
}
}
我遇到的问题是我的前端只是将资源 ID 发送到我的端点。@IsGranted
Symfony 然后通过调用 Voters 并传入属性"DELETE_POST"
和帖子 ID来解析注释。
问题是,$post 只是一个帖子 ID,而不是一个实际的 Post 对象。因此,当 Voter 到达时,$subject instanceof Post
它会返回false
。
我尝试Post
通过将方法签名更改为public function deletePost(Post $post)
. 当然这不起作用,因为 javascript 在 URL 中发送一个 id,而不是 Post 对象。
(顺便说一句:我知道这种类型的注射应该适用于 Doctrine,但我没有使用 Doctrine)。
我的问题是我如何@IsGranted
理解“帖子”应该是一个帖子对象?有没有办法告诉它从传入的 id 中查找 Post 并基于它进行评估?或者甚至遵从另一个控制器方法来确定subject="post"
应该代表什么?
谢谢。
更新
感谢@NicolasB,我添加了一个 ParamConverter:
class PostConverter implements ParamConverterInterface {
private $dao;
public function __construct(MySqlPostDAO $dao) {
$this->dao = $dao;
}
public function apply(Request $request, ParamConverter $configuration) {
$name = $configuration->getName();
$object = $this->dao->getById($request->get("id"));
if (!$object) {
throw new NotFoundHttpException("Post not found!");
}
$request->attributes->set($name, $object);
return true;
}
public function supports(ParamConverter $configuration) {
if ($configuration->getClass() === "App\\Model\\Objects\\Post") {
return true;
}
return false;
}
}
这似乎按预期工作。我什至不必使用 @ParamConverter 注释来使其工作。我必须对控制器进行的唯一其他更改是更改我的路由的方法签名public function deletePost(Post $post)
(正如我之前尝试过的那样 - 但现在由于我的PostConverter
.
我最后的两个问题是:
我应该在
supports()
方法中检查什么?我目前只是检查班级是否匹配。我是否也应该检查一下$configuration->getName() == "id"
,以确保我使用的是正确的字段?我怎样才能使它更通用?我是否正确假设任何时候你在控制器方法中注入一个实体,Symfony 会
supports
在所有实现的东西上调用该方法ParamConverterInterface
?
谢谢。
解决方案
如果您使用 Doctrine 会发生什么,您需要对$post
变量进行类型提示。完成之后,Doctrine 的ParamConverter会处理剩下的事情。目前,Symfony 不知道如何将您的id
url 占位符与您的$post
参数相关联,因为它不知道 Entity$post
指的是哪个。通过使用类似的类型提示public function deletePost(Post $post)
并使用 ParamConverter,Symfony 会知道它$post
指的是具有来自 url占位符Post
的 id 的实体。id
从文档:
通常,您希望 show() 有一个 $id 参数。相反,通过创建一个新参数 ($post) 并使用 Post 类(它是一个 Doctrine 实体)对其进行类型提示,ParamConverter 会自动查询 $id 属性与 {id} 值匹配的对象。如果找不到帖子,它也会显示 404 页面。
选民也将知道$post
它是什么以及如何处理它。
现在,由于您没有使用 Doctrine,因此默认情况下您没有 ParamConverter,正如我们刚刚看到的,这是这里的关键元素。所以你要做的只是定义你自己的 ParamConverter。
Symfony 文档的这一页将告诉你更多关于如何做到这一点,尤其是最后一节“创建转换器”。您必须告诉它如何使用模型的逻辑将字符串"id"
转换为对象。Post
首先,您可以使其非常特定于Post
对象(并且您可能希望使用该converter="name"
选项在注释中显式引用该 ParamConverter)。稍后,一旦你有了一个工作版本,你可以让它更通用。
推荐阅读
- javascript - iframe 未使用 react-iframe 加载
- android-espresso - 单元测试下的 Robolectric 点击视图是否可行?
- http-live-streaming - 使用 nginx 和 video,js 在 http 实时流中假设质量选择器的问题
- youtube - 我们的 YouTube 数据 API 密钥出现错误“accessNotConfigured”
- speech-recognition - Web SpeechRecognizer 与 Android/iOS SpeechRecognizer 相比如何?
- php - 如何创建支持 UTF-8 的类似 Quora 的 URL?
- java - 客户注册 ID 所需的授权
- java - 为什么当我尝试将我的 Spring Boot 应用程序推送到 heroku 时它返回“致命错误编译:无效目标版本:11”
- angular - 在Angular中删除传单标记不起作用
- typescript - 如何使用打字稿在量角器和黄瓜中传递动态xpath