我在 Drupal 8 中使用标志模块面临问题(csrf_token' URL 查询参数无效)

Posted

技术标签:

【中文标题】我在 Drupal 8 中使用标志模块面临问题(csrf_token\' URL 查询参数无效)【英文标题】:I am facing problem (csrf_token' URL query argument is invalid) with flag module at Drupal 8我在 Drupal 8 中使用标志模块面临问题(csrf_token' URL 查询参数无效) 【发布时间】:2021-03-01 15:31:20 【问题描述】:

我生成了标志链接

  $flag_link = [
  '#lazy_builder' => ['flag.link_builder:build', [
    $product->getEntityTypeId(),
    $product->id(),
    'product_like',
  ]],
   '#create_placeholder' => TRUE,
];

标志链接生成成功。但是当我点击标志链接时,我收到错误消息作为响应

message: "'csrf_token' URL query argument is invalid."
message: "'csrf_token' URL query argument is invalid."

【问题讨论】:

临时我通过修改 modules/contrib/flag/src/Access/CsrfAccessCheck.php 解决了这个问题只需删除条件: return $this->account->isAnonymous() ? AccessResult::allowed() : $this->original->access($route, $request, $route_match);并添加条件:return AccessResult::allowed(); 以上解决方案不好。请任何人以适当的方式解决这个问题.. Drupal 核心问题:"nojs"/"ajax" route parameter in use-ajax link breaks CSRF protection 【参考方案1】:

我找到了一个临时解决方案。不确定这是否是需要由模块维护人员解决的 Flag 中的错误,或者这是否按预期工作,因为这是 REST 响应,而不是典型的 Drupal 调用视图或显示模式。

在 ModuleRestResource.php 文件中(在我的例子中,ModuleRestResource.php 文件位于: DRUPAL_ROOT/web/modules/custom/Module_Name/src/Plugin/rest/resource/Module_NameRestResource.php):

use Drupal\rest\ModifiedResourceResponse;
use Drupal\rest\Plugin\ResourceBase;
use Drupal\rest\ResourceResponse;
use Drupal\Core\Entity\EntityTypeManager;
use Drupal\Core\Entity\EntityInterface;
use Drupal\flag\FlagService;
use Drupal\Core\Render\RenderContext;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;

class ModuleRestResource extends ResourceBase 

  /**
   * A current user instance.
   *
   * @var \Drupal\Core\Session\AccountProxyInterface
   */
  protected $currentUser;

  /**
   * @var $entityTypeManager \Drupal\Core\Entity\EntityTypeManager
   */
  protected $entityTypeManager;

  /**
   * @var \Drupal\flag\FlagService
   */
  protected $flagService;

  /**
   * @var Drupal\Core\Access\CsrfTokenGenerator
   */
  protected $csrfService;

  /**
   * @inheritdoc
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) 
    $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
    $instance->logger = $container->get('logger.factory')->get('module');
    $instance->currentUser = $container->get('current_user');
    $instance->entityTypeManager = $container->get('entity_type.manager');
    $instance->flagService = $container->get('flag');
    $instance->csrfService = $container->get('csrf_token');
    return $instance;
  

  /**
   * Responds to GET requests.
   *
   * @param string $payload
   *
   * @return \Drupal\rest\ResourceResponse
   *   The HTTP response object.
   *
   * @throws \Symfony\Component\HttpKernel\Exception\HttpException
   *   Throws exception expected.
   */
  public function get($payload) 

    // You must to implement the logic of your REST Resource here.
    // Use current user after pass authentication to validate access.
    if (!$this->currentUser->hasPermission('access content')) 
      throw new AccessDeniedHttpException();
    

    if (!is_numeric($payload)) 
      throw new BadRequestHttpException();
    

    /*
     * This is the object that will be returned with the node details.
     */
    $obj = new \stdClass();

    // First load our node.
    /**
     * @var \Drupal\Core\Entity\EntityInterface
     */
    $node = $this->entityTypeManager->getStorage('node')->load($payload);

    /**
     * FIX STARTS HERE !!!!!
     */

    /**
     * Because we are rending code early in the process, we need to wrap in executeInRenderContext
     */

    $render_context = new RenderContext();
    $fl = \Drupal::service('renderer')->executeInRenderContext($render_context, function() use ($node, $payload) 

      /**
       * Get the flag we need and check if the selected node has been flagged by the current user
       *
       * Set the path to create a token. This is the value that is missing by default that creates an
       * invalid CSRF Token. Important to note that the leading slash should be left off for token generation
       * and then added to to the links href attribute
       *
       */
      $flag = $this->flagService->getFlagById('bookmark');
      $is_flagged = (bool) $this->flagService->getEntityFlaggings($flag, $node, \Drupal::currentUser() );
      $path = 'flag/'. ($is_flagged ? 'un' : '') .'flag/bookmark/' . $node->id();
      $token = $this->csrfService->get($path);
      $flag_link = $flag->getLinkTypePlugin()->getAsFlagLink($flag, $node);
      $flag_link['#attributes']['href'] = '/' . $path . '?destination&token=' . $token;

      /**
       * Render the link into html
       */
      return \Drupal::service('renderer')->render($flag_link);
    );

    /**
     * This is required to bubble metadata
     */
    if (!$render_context->isEmpty()) 
        $bubbleable_metadata = $render_context->pop();
        \Drupal\Core\Render\BubbleableMetadata::createFromObject($fl)
            ->merge($bubbleable_metadata);
    
    /*
     * !!!!! FIX ENDS HERE !!!!!
     */

    $obj->flag_link = $fl;

    return new ResourceResponse((array)$obj, 200);
  


任何可以让模块维护者解决这个问题的人都会很好。

【讨论】:

以上是关于我在 Drupal 8 中使用标志模块面临问题(csrf_token' URL 查询参数无效)的主要内容,如果未能解决你的问题,请参考以下文章

快速编辑模块在drupal 7.5中不起作用

我在 Drupal 的测验模块(Drupal 7,测验版本 7.x-5.2)中看不到反馈

如何从 Drupal 7 向 Drupal 8 贡献一个模块

无法通过 Drupal 8 发送电子邮件

Drupal 8/9 模块发布后没有刷新表单数据

如何 - 构建自定义 drupal 8 模块表单