滚动
使用事件订阅器限制对分类术语词汇表的访问权限
有时你需要在站点上设置固定的、永久性的分类(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
:用于快速检查当前用户角色。
3. 订阅的事件
- 该类订阅了
KernelEvents::REQUEST
事件,优先级为 32。- 确保路由参数已解析。
- 但控制器尚未执行,可拦截请求并重定向。
4. 重定向逻辑
onKernelRequest()
方法执行访问检查和重定向逻辑:- 仅处理主请求: 避免重复处理。
- 管理员跳过重定向: 拥有
administrator
角色的用户可以正常访问。 - 匹配指定路由: 仅处理与 taxonomy 和 term 编辑相关的后台页面。
- 重定向非管理员:
- 对于词汇表概览、添加或列表路由,确认其为
tag
。 - 对于术语编辑或删除路由,通过
bundle()
检查其是否属于tag
。
- 对于词汇表概览、添加或列表路由,确认其为
- 使用 TrustedRedirectResponse: 安全地重定向用户至“tag”词汇表概览页面。
- 可扩展性: 可轻松扩展以支持多个词汇表或更复杂的角色判断。
5. 安全性与最佳实践
- 请求早期拦截: 在控制器执行前阻止访问,提高安全性。
- 基于角色的跳过机制: 不干扰管理员或开发者工作流程。
- 关注点分离: 路由判断、用户检查、重定向逻辑各自独立,便于维护。
6. 潜在增强
- 可以利用
EntityTypeManagerInterface
执行更复杂的实体或权限逻辑。 - 也可以将此类泛化以支持多个词汇表,并允许通过配置指定重定向路径。
7. 关键总结
- 该 事件订阅器 展示了如何借助 Symfony 的事件系统,在 Drupal 中高效地实现访问控制。
- 它非常适合保护不应频繁更改的分类词汇表,防止普通用户意外修改。