Drupal 9如何无需安装模块实现登录后重定向功能
Drupal 9.3 版本对重定向安全校验、表单提交流程做了调整,这是之前用的Login Destination等第三方模块失效的核心原因——多数模块未适配新的安全拦截规则,未做内部路径校验的重定向会被核心直接拦截回退到默认用户主页。核心本身已经原生支持destination参数登录跳转,不需要额外安装模块,按以下步骤排查配置即可:
第一步:检查登录表单模板是否遗漏destination字段输出
如果你定制过登录表单模板(比如user-login.html.twig、或者在页面模板里手动渲染登录块),必须保留{{ form.destination }}的输出。核心构建登录表单时会自动读取当前URL的destination参数,写入这个隐藏提交字段,很多人定制模板时只输出账号、密码、提交按钮,直接漏掉了这个字段,参数自然不会传递到提交逻辑里。第二步:如果有自定义登录路由、第三方登录拦截逻辑,加轻量事件订阅器兜底
如果你没有用核心默认的/user/login路由,或者站点装了SSO、社交登录类模块重写了登录提交流程,写一个几十行的自定义事件订阅器就能实现逻辑,兼容性比第三方模块好:- 在你现有的自定义模块目录下,新建
src/EventSubscriber/LoginRedirectSubscriber.php,写入以下代码:
<?php namespace Drupal\your_custom_module\EventSubscriber; use Drupal\Core\Url; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpKernel\Event\ResponseEvent; use Symfony\Component\HttpKernel\KernelEvents; class LoginRedirectSubscriber implements EventSubscriberInterface { public static function getSubscribedEvents() { // 优先级设为100,优先于其他模块的重定向逻辑执行 $events[KernelEvents::RESPONSE] = ['onKernelResponse', 100]; return $events; } public function onKernelResponse(ResponseEvent $event) { $request = $event->getRequest(); $response = $event->getResponse(); $route = $request->attributes->get('_route'); // 只处理核心登录路由的提交响应 if (!in_array($route, ['user.login', 'user.login.http'])) return; if (!$response instanceof RedirectResponse) return; $destination = $request->query->get('destination'); if (empty($destination)) return; // 走核心自带的路径校验,过滤外部恶意地址,避免开放重定向漏洞 $target_url = Url::fromUserInput($destination); if ($target_url->isRouted() && !$target_url->isExternal()) { $response->setTargetUrl($target_url->toString()); } } }- 在自定义模块根目录的
your_custom_module.services.yml里注册服务:
services: your_custom_module.login_redirect_subscriber: class: Drupal\your_custom_module\EventSubscriber\LoginRedirectSubscriber tags: - { name: event_subscriber }- 在你现有的自定义模块目录下,新建
第三步:修复缓存导致的参数不生效问题
如果站点开了动态页缓存、内部页面缓存,需要给登录页增加query参数的缓存上下文,避免带destination的页面被通用缓存覆盖。在自定义模块的.module文件里加入以下代码:<?php /** * Implements hook_page_attachments_alter(). */ function your_custom_module_page_attachments_alter(array &$attachments) { if (\Drupal::service('path.current')->getPath() === '/user/login') { \Drupal::service('renderer')->addCacheContexts(['url.query_args:destination']); } }
所有代码部署完成后执行drush cr清空缓存即可生效。注意不要直接读取$_GET变量做跳转,必须用核心的Url类做路径校验,否则会被Drupal 9的安全防护机制拦截,这也是之前测试的几个第三方模块普遍存在的兼容问题。
内容的提问来源于stack exchange,提问作者Franco Martinez

