logo

额外区块类型 (EBT) - 全新的布局构建器体验❗

额外区块类型 (EBT) - 样式化、可定制的区块类型:幻灯片、标签页、卡片、手风琴等更多类型。内置背景、DOM Box、JavaScript 插件的设置。立即体验布局构建的未来。

演示 EBT 模块 下载 EBT 模块

❗额外段落类型 (EPT) - 全新的 Paragraphs 体验

额外段落类型 (EPT) - 类似的基于 Paragraph 的模块集合。

演示 EPT 模块 滚动

滚动

使用事件订阅器限制对分类术语词汇表的访问权限

02/09/2025, by Ivan

Menu

有时你需要在站点上设置固定的、永久性的分类(Categories),这些分类不应被意外修改。在这种情况下,你可以利用自定义代码和事件订阅器(Event Subscriber)来实现。

我们将在自定义模块中添加一个新的 Event Subscriber 类。

drupalbook_custom.services.yml

services:  
  drupalbook_custom.tag_redirect_subscriber:
    class: Drupal\drupalbook_custom\EventSubscriber\TagRedirectSubscriber
    arguments:
      - '@entity_type.manager'
      - '@current_user'
    tags:
      - { name: event_subscriber }

然后在 drupalbook_custom/src/EventSubscriber/TagRedirectSubscriber 中添加我们的事件订阅器类:

<?php

namespace Drupal\drupalbook_custom\EventSubscriber;

use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Routing\TrustedRedirectResponse;
use Drupal\Core\Session\AccountProxyInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;

/**
 * 将非管理员从 Tag 词汇表的后台页面重定向。
 */
class TagRedirectSubscriber implements EventSubscriberInterface {

  protected EntityTypeManagerInterface $entityTypeManager;
  protected AccountProxyInterface $currentUser;

  public function __construct(
    EntityTypeManagerInterface $entity_type_manager,
    AccountProxyInterface $current_user,
  ) {
    $this->entityTypeManager = $entity_type_manager;
    $this->currentUser = $current_user;
  }

  public static function getSubscribedEvents(): array {
    return [
      KernelEvents::REQUEST => ['onKernelRequest', 32],
    ];
  }

  public function onKernelRequest(RequestEvent $event): void {
    if (!$event->isMainRequest()) {
      return;
    }

    if ($this->currentUser->hasRole('administrator')) {
      return;
    }

    $request = $event->getRequest();
    $route_name = $request->attributes->get('_route');

    $redirect_to = 'https://drupalbook.org/admin/structure'
      . '/taxonomy/manage/tag/overview';

    switch ($route_name) {
      case 'entity.taxonomy_vocabulary.overview_form':
      case 'entity.taxonomy_vocabulary.overview_terms':
      case 'entity.taxonomy_term.add_form':
        $vocabulary = $request->attributes->get('taxonomy_vocabulary');
        if (!empty($vocabulary) && $vocabulary->id() === 'tag') {
          $event->setResponse(new TrustedRedirectResponse($redirect_to));
        }
        return;

      case 'entity.taxonomy_term.edit_form':
      case 'entity.taxonomy_term.delete_form':
        $term = $request->attributes->get('taxonomy_term');
        if ($term && $term->bundle() === 'tag') {
          $event->setResponse(new TrustedRedirectResponse($redirect_to));
        }
        return;

      default:
        return;
    }
  }

}

TagRedirectSubscriber 类是一个 Drupal 的自定义 事件订阅器,用于限制非管理员访问特定分类词汇表(本例中为 "tag")的后台页面。以下是其结构和关键点解析:

1. 目的与使用场景

  • 目标: 防止非管理员用户意外或未经授权修改 “tag” 词汇表。
  • 好处: 提供 UI 层面的访问控制,保护关键分类不被更改。

2. 类结构与依赖

  • 该类实现了 EventSubscriberInterface,与 Drupal 所用的 Symfony 事件系统兼容。
  • 通过构造函数注入的依赖:
    • EntityTypeManagerInterface:当前逻辑未使用,作为可扩展性保留。
    • AccountProxyInterface:用于快速检查当前用户角色。
    依赖注入使该订阅器可测试、可重用,符合 Drupal 服务容器最佳实践。

3. 订阅的事件

  • 该类订阅了 KernelEvents::REQUEST 事件,优先级为 32
    • 确保路由参数已解析。
    • 但控制器尚未执行,可拦截请求并重定向。

4. 重定向逻辑

  • onKernelRequest() 方法执行访问检查和重定向逻辑:
    • 仅处理主请求: 避免重复处理。
    • 管理员跳过重定向: 拥有 administrator 角色的用户可以正常访问。
    • 匹配指定路由: 仅处理与 taxonomy 和 term 编辑相关的后台页面。
    • 重定向非管理员:
      • 对于词汇表概览、添加或列表路由,确认其为 tag
      • 对于术语编辑或删除路由,通过 bundle() 检查其是否属于 tag
    • 使用 TrustedRedirectResponse: 安全地重定向用户至“tag”词汇表概览页面。
  • 可扩展性: 可轻松扩展以支持多个词汇表或更复杂的角色判断。

5. 安全性与最佳实践

  • 请求早期拦截: 在控制器执行前阻止访问,提高安全性。
  • 基于角色的跳过机制: 不干扰管理员或开发者工作流程。
  • 关注点分离: 路由判断、用户检查、重定向逻辑各自独立,便于维护。

6. 潜在增强

  • 可以利用 EntityTypeManagerInterface 执行更复杂的实体或权限逻辑。
  • 也可以将此类泛化以支持多个词汇表,并允许通过配置指定重定向路径。

7. 关键总结

  • 事件订阅器 展示了如何借助 Symfony 的事件系统,在 Drupal 中高效地实现访问控制。
  • 它非常适合保护不应频繁更改的分类词汇表,防止普通用户意外修改。