CSRF保护-令牌无效|在一个页面上有两个与Symfony中的同一实体相关的表单

fhity93d  于 2023-03-19  发布在  其他
关注(0)|答案(2)|浏览(114)

除非我在FormType文件中将csrf_protection参数切换为true,否则传递给每个表单的数据都会正确地添加到DB中。
CSRF令牌无效。请尝试重新提交表单。
为什么会发生这种情况?有人能告诉我应该如何修复这个问题吗?我正在使用Symfony助手创建表单,这意味着csrf令牌应该自动呈现。
我尝试使用form_row()form_widget()函数分别呈现字段,但没有任何帮助。

注解类型.php

class CommentType extends AbstractType
{

    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('content', TextareaType::class)
            ->add('submit', SubmitType::class)
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Comment::class,
            'csrf_protection' => true,
            'csrf_field_name' => 'token',
            'csrf_token_id'   => 'comment_csrf_id'
        ]);
    }

}

评论控制器.php

class CommentController extends AbstractController
{

    private EntityManagerInterface $entityManager;

    public function __construct(EntityManagerInterface $entityManager)
    {
        $this->entityManager = $entityManager;

    }

    /**
     * @Route("/comments", name="app_comments")
     */
    public function indexAction(Request $request): Response
    {

        $user = $this->getUser();

        $comment = new Comment();

        $commentForm = $this->createForm(CommentType::class, $comment);

        $commentForm->handleRequest($request);

        if ($commentForm->isSubmitted() && $commentForm->isValid()) {

            $this->addComment($comment, $user, $request->request->get('parent_id') ?? null);

            $this->addFlash('success', 'Commentary added');

            return $this->redirectToRoute('app_comments');
        }

        $allComments = $this->entityManager->getRepository(Comment::class)->findAll();

        $sortedComments = $this->sortComments($allComments);

        return $this->renderForm('comments/index.html.twig', [
            'commentForm' => $commentForm,
            'sortedComments' => $sortedComments,
        ]);

    }

index.html.twig(具有父窗体的文件)

{% block body %}
    <section class="gradient-custom">
        <div class="container my-2 py-2">
            <div class="row d-flex justify-content-center">
                <div class="col-md-12 col-lg-10 col-xl-8 mb-4">
                    <div class="card">
                        <div class="card-body p-4">
                            <h3 class="text-center mb-4 pb-2">Tree Comments</h3>
                            <div class="row">
                                <div class="col">
                                    {% for comment in sortedComments %}
                                        {% set templateAttrs = [comment.getId, asset('avatars/avatar-6.webp'), asset('avatars/avatar-7.webp')] %}
                                        {% include 'comments/comment.html.twig' with { comment: comment, commentIdValue: templateAttrs.0, userImage: templateAttrs.1} %}
                                        {% for child in comment.children %}
                                            <div class="mt-3 ml-5">
                                                {% include 'comments/comment.html.twig' with { comment: child, commentIdValue: templateAttrs.0, userImage: templateAttrs.2} %}
                                            </div>
                                        {% endfor %}
                                        <hr>
                                    {% endfor %}
                                </div>
                            </div>
                        </div>
                        {% if app.user == true %}
                            {{ form_start(commentForm, {'attr': {'class': 'ml-4 mr-4 mb-4 pb-2'}}) }}
                            <div class="form-group">
                                {{ form_label(commentForm.content, 'Your Commentary') }}
                                <textarea class="form-control"
                                          name="{{ field_name(commentForm.content) }}"
                                          rows="2"
                                          required>
                                </textarea>
                                {{ form_errors(commentForm.content) }}
                            </div>
                            <div class="submit">
                                <input class="btn btn-primary"
                                       name="{{ field_name(commentForm.submit) }}"
                                       type="submit"
                                       value="Submit">
                            </div>
                            {{ form_end(commentForm) }}
                        {% endif %}
                        {% if app.user == false %}
                            <div class="text-center mb-2 pb-2">
                                <p>To leave your commentary, please <a href="{{ path('app_login') }}">log in</a></p>
                            </div>
                        {% endif %}
                    </div>
                </div>
            </div>
        </div>
        <script>
        </script>
    </section>
{% endblock %}

comment.html.twig(它有子窗体,并且此文件包含在index.html.twig中)

<div class="d-flex flex-start mb-1">
    <a class="me-3" href="javascript:void(0)">
        <img class="rounded-circle shadow-1-strong mr-2"
             src="{{ userImage }}" alt="Random Image" width="65"
             height="65"/>
    </a>
    <div class="flex-grow-1 flex-shrink-1">
        <div class="col-11 p-0 d-inline-block comment">
            <div class="d-flex justify-content-between align-items-center">
                <p class="mb-1">
                    {{ comment.user.userIdentifier }}
                    <span class="small">- {{ comment.createdAt|ago }}  </span>
                </p>
            </div>
            <p class="small mb-0">
                {% if comment.isDeleted == true %}
                    <i> *This commentary was deleted by moderator* </i>
                {% else %}
                    {{ comment.content }}
                {% endif %}
            </p>
            {% if is_granted('IS_AUTHENTICATED_FULLY') and comment.isDeleted == false %}
                {% set isAlreadyLikedComment = '' %}
                {% for like in comment.getCommentLikes %}
                    {% set isAlreadyLikedComment = like.user.getId %}
                {% endfor %}
                <div class="js-likes-unlikes">
                    <a href="{{ path('app_comment_like_unlike') }}"
                       id="comment-like"
                       data-entity-id="{{ comment.id }}"
                       data-csrf-token="{{ csrf_token('comment' ~ comment.id) }}"
                       data-liked="{{ isAlreadyLikedComment == app.user.id ? '1' : '0' }}"
                       data-likes-counter="{{ comment.commentLikes.count }}"
                       class="btn-like">
                        <i class="comment-like-{{ comment.id }}">
                            {{ isAlreadyLikedComment == app.user.id ? '♥' : '♡' }}
                        </i>
                    </a>
                    <span class="counter small mb-0"
                          id="count-likes-{{ comment.id }}">{{ comment.commentLikes.count }}</span>
                </div>
        </div>
        <div class="col-1 p-0 float-right d-flex align-items-center flex-column">
            <a href="javascript:void(0)"
               onclick="document.getElementById('formreply-{{ comment.getId }}').style.display='';">
                <span class="small">reply</span>
                {% if is_granted('ROLE_ADMIN') %}
                    <a href="{{ path('delete_comment',{'id': comment.getId }) }}">
                        <span class="small"> delete</span>
                    </a>
                {% endif %}
            </a>
            {% endif %}
        </div>
    </div>
</div>
{% set formAttrs = ['m2 pb-2', 'formreply-' ~ comment.getId, 'display: none !important;'] %}
    {% if app.user == true %}
<div class="d-flex flex-column">
    <div class="flex-grow-1 flex-shrink-1 reply-form">
        {{ form_start(commentForm, {'attr': {'class': formAttrs.0, 'id': formAttrs.1, 'style': formAttrs.2 }}) }}
        <div class="form-group">
            {{ form_label(commentForm.content, 'Your Commentary') }}
            <textarea class="form-control"
                      name="{{ field_name(commentForm.content) }}" required>@{{ comment.user.userIdentifier }}, </textarea>
            {{ form_errors(commentForm.content) }}
        </div>
        <div class="submit">
            <input class="btn btn-primary"
                   name="{{ field_name(commentForm.submit) }}"
                   type="submit"
                   value="Submit">
        </div>
        <input type="hidden"
               name="parent_id"
               value="{{ commentIdValue }}">
        {{ form_end(commentForm) }}
    </div>
    {% endif %}
</div>

在这种情况下,可以将两个表单绑定到同一个实体吗?我也尝试创建两个formType文件,但将它们绑定到同一个实体,这只起到了部分作用:父窗体正确提交,但子窗体保留错误:
CSRF令牌无效。请尝试重新提交表单。

d8tt03nd

d8tt03nd1#

这可能与在同一页上呈现或处理表单的方式有关。要解决此问题,可以尝试以下步骤:
1.更新您的CommentType.php,使父窗体和子窗体都具有唯一的CSRF令牌:

public function configureOptions(OptionsResolver $resolver)
{
    $resolver->setDefaults([
        'data_class' => Comment::class,
        'csrf_protection' => true,
        'csrf_field_name' => '_token',
        'csrf_token_id'   => 'comment_'.uniqid()
    ]);
}

这将为每个表单示例生成唯一的CSRF令牌。
1.确保使用form_row()form_widget()函数呈现CSRF令牌,因为您提到您已经尝试过了,所以我假设这已经到位。
1.进行以下更改后,请记住清除Symfony缓存:

php bin/console cache:clear

告诉我有没有帮助。

oxcyiej7

oxcyiej72#

尝试为父窗体和子窗体生成唯一的CSRF标记ID。

评论控制器.php

class CommentController extends AbstractController
{

    private EntityManagerInterface $entityManager;

    public function __construct(EntityManagerInterface $entityManager)
    {
        $this->entityManager = $entityManager;

    }

    /**
     * @Route("/comments", name="app_comments")
     */
    public function indexAction(Request $request): Response
    {

        $user = $this->getUser();

        $parentComment = new Comment();
        $childComment = new Comment();

        $parentCommentForm = $this->createForm(CommentType::class, $parentComment, [
            'csrf_token_id' => 'parent_comment_csrf_id',
        ]);

        $childCommentForm = $this->createForm(CommentType::class, $childComment, [
            'csrf_token_id' => 'child_comment_csrf_id',
        ]);

        $parentCommentForm->handleRequest($request);
        $childCommentForm->handleRequest($request);

        if ($parentCommentForm->isSubmitted() && $parentCommentForm->isValid()) {

            $this->addComment($parentComment, $user, $request->request->get('parent_id') ?? null);

            $this->addFlash('success', 'Commentary added');

            return $this->redirectToRoute('app_comments');
        }

        if ($childCommentForm->isSubmitted() && $childCommentForm->isValid()) {

            $this->addComment($childComment, $user, $request->request->get('parent_id') ?? null);

            $this->addFlash('success', 'Commentary added');

            return $this->redirectToRoute('app_comments');
        }

        $allComments = $this->entityManager->getRepository(Comment::class)->findAll();

        $sortedComments = $this->sortComments($allComments);

        return $this->renderForm('comments/index.html.twig', [
            'parentCommentForm' => $parentCommentForm->createView(),
            'childCommentForm' => $childCommentForm->createView(),
            'sortedComments' => $sortedComments,
        ]);

    }
}

更新您的Twig模板以使用新的表单示例parentCommentForm和childCommentForm instead of the previous single form instance commentForm '。

索引.html.小树枝

{{ form_start(parentCommentForm, {'attr': {'class': 'ml-4 mr-4 mb-4 pb-2'}}) }}
    <div class="form-group">
        {{ form_label(parentCommentForm.content, 'Your Commentary') }}
        <textarea class="form-control"
                  name="{{ field_name(parentCommentForm.content) }}"
                  rows="2"
                  required>
        </textarea>
        {{ form_errors(parentCommentForm.content) }}
    </div>
    <div class="submit">
        <input class="btn btn-primary"
               name="{{ field_name(parentCommentForm.submit) }}"
               type="submit"
               value="Submit">
    </div>
    {{ form_end(parentCommentForm) }}

评论.html.twig

{{ form_start(childCommentForm, {'attr': {'class': formAttrs.0, 'id': formAttrs.1, 'style': formAttrs.2 }}) }}
<div class="form-group">
    {{ form_label(childCommentForm.content, 'Your Commentary') }}
    <textarea class="form-control"
              name="{{ field_name(childCommentForm.content) }}" required>@{{ comment.user.userIdentifier }}, </textarea>
    {{ form_errors(childCommentForm.content) }}
</div>
<div class="submit">
    <input class="btn btn-primary"
           name="{{ field_name(childCommentForm.submit) }}"
           type="submit"
           value="Submit">
</div>
<input type="hidden"
       name="parent_id"
       value="{{ commentIdValue }}">
{{ form_end(childCommentForm) }}

相关问题