Symfony 4选民注解(@IsGranted)

时间:2018-10-01 23:44:33

标签: php symfony annotations authorization

我正在尝试使用Symfony选民和控制器注释来允许或限制对Symfony 4应用程序中某些操作的访问。

例如,“我的前端”提供了删除“帖子”的功能,但前提是用户为该帖子设置了“

前端将HTTP“ DELETE”操作发送到我的symfony端点,并在URL(即/api/post/delete/19)中传递帖子的ID。

我正在尝试使用here中所述的@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发送到端点。然后,Symfony通过调用选民并传递属性@IsGranted和帖子ID来解析"DELETE_POST"注释。

问题是,$ post只是一个帖子ID,而不是实际的Post对象。因此,当选民到达$subject instanceof Post时,它将返回false

我尝试通过将方法签名更改为Postpublic function deletePost(Post $post)注入我的控制器方法。当然,这是行不通的,因为javascript是在URL中发送ID,而不是Post对象。

(顺便说一句:我知道这种注射方式应该适用于Doctrine,但我不是使用Doctrine)。

我的问题是我如何得到@IsGranted来理解“发布”应该是发布对象?有没有办法告诉它从传入的ID并根据该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而起作用)。

我的最后两个问题是:

  1. 我应该在supports()方法中检查什么?我目前正在检查类是否匹配。我是否还应该检查$configuration->getName() == "id",以确保使用正确的字段?

  2. 如何使它变得更通用?我是否假设您每次在控制器方法中注入实体,Symfony都会在实现supports的所有对象上调用ParamConverterInterface方法?

谢谢。

1 个答案:

答案 0 :(得分:2)

如果您使用Doctrine将会发生什么,那就是您需要键入提示$post变量。完成之后,Doctrine的ParamConverter将负责其余的工作。目前,Symfony不知道如何将id url占位符与$post参数相关联,因为它不知道实体$post是指哪个实体。通过使用类似public function deletePost(Post $post)的类型进行提示并使用ParamConverter,Symfony将知道$post引用了Post实体,其ID来自URL的id占位符。 / p>

来自文档:

  

通常,您希望在show()中使用$ id参数。相反,通过创建一个新参数($ post)并用Post类(这是一个Doctrine实体)进行类型提示,ParamConverter会自动查询其$ id属性与{id}值匹配的对象。如果找不到帖子,它也会显示404页面。

然后选民还将知道$post是什么以及如何对待它。


现在,由于您不使用Doctrine,因此默认情况下没有ParamConverter,正如我们刚刚看到的,这是这里的关键要素。因此,您要做的就是简单地定义自己的ParamConverter。

Symfony文档的

This page将告诉您更多有关如何执行此操作的信息,尤其是最后一节“创建转换器”。您将必须告诉它如何使用模型的逻辑将字符串"id"转换为Post对象。首先,您可以使其非常特定于Post对象(并且您可能希望使用converter="name"选项在注释中显式引用该ParamConverter)。稍后,当您有了可用的版本后,就可以使其更通用。