You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Symfony 6.2会话过期后无法重定向问题求助

Symfony 6.2 会话超时监听器无法跳转的修复方案

核心问题排查

  1. 事件类型不匹配:你订阅了KernelEvents::RESPONSE事件,但onRequest方法接收的是RequestEvent——RESPONSE事件对应的是ResponseEvent,这直接导致方法无法正确触发,setResponse自然无效。
  2. 事件阶段错误:会话超时检测应该在请求处理的早期(KernelEvents::REQUEST阶段)执行,响应阶段已经接近请求处理完成,此时设置的响应很容易被后续逻辑覆盖。
  3. 依赖缺失:你尝试用UrlGenerator但没在构造函数中注入,导致无法正常生成路由URL。

修复后的监听器代码

namespace App\EventListener;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;

class SessionTimeoutListener implements EventSubscriberInterface
{
    private $requestStack;
    private $csrfTokenManager;
    private $urlGenerator;

    public function __construct(
        CsrfTokenManagerInterface $csrfTokenManager,
        RequestStack $requestStack,
        UrlGeneratorInterface $urlGenerator
    ) {
        $this->requestStack = $requestStack;
        $this->csrfTokenManager = $csrfTokenManager;
        $this->urlGenerator = $urlGenerator;
    }

    public static function getSubscribedEvents()
    {
        return [
            // 改用REQUEST事件,高优先级确保早执行
            KernelEvents::REQUEST => [['onRequest', 100]],
            KernelEvents::RESPONSE => [['refreshSessionCookie', 0]],
        ];
    }

    public function refreshSessionCookie(ResponseEvent $event)
    {
        $request = $event->getRequest();
        if (!$request->hasSession()) {
            return;
        }
        $session = $request->getSession();
        if ($session->isStarted() && $event->getResponse()) {
            $response = $event->getResponse();
            $cookieLifetime = 20;
            // 复用会话原有Cookie参数,避免不匹配
            $cookie = new \Symfony\Component\HttpFoundation\Cookie(
                $session->getName(),
                $session->getId(),
                time() + $cookieLifetime,
                $session->getCookieParams()['path'],
                $session->getCookieParams()['domain'],
                $session->getCookieParams()['secure'],
                $session->getCookieParams()['httponly'],
                false,
                $session->getCookieParams()['samesite']
            );
            $response->headers->setCookie($cookie);
        }
    }

    public function onRequest(RequestEvent $event)
    {
        $request = $event->getRequest();

        // 跳过子请求,只处理主请求
        if (!$event->isMainRequest()) {
            return;
        }
        // 检查会话状态
        if (!$request->hasSession() || !$request->getSession()->isStarted()) {
            return;
        }
        // 跳过未认证用户
        if (!$request->getSession()->has('_security_main')) {
            return;
        }
        // 跳过未接受Cookie的请求
        if (!$request->cookies->has('accepted_cookies')) {
            return;
        }

        $session = $request->getSession();
        $lastUsedTimestamp = $session->getMetadataBag()->getLastUsed();
        $currentTime = time();
        $sessionTimeout = 20;

        if ($currentTime - $lastUsedTimestamp > $sessionTimeout) {
            $session->invalidate();
            $this->csrfTokenManager->refreshToken('security.csrf.token_manager');
            
            // 用路由生成器生成正确跳转地址
            $logoutUrl = $this->urlGenerator->generate('homepage_default', ['_locale' => 'cs']);
            $response = new RedirectResponse($logoutUrl);
            $event->setResponse($response);
        }
    }
}

配置调整

  1. 服务配置更新:补上UrlGenerator的注入:
# config/services.yaml
App\EventListener\SessionTimeoutListener:
    tags:
        - { name: kernel.event_subscriber }
    arguments:
        - '@security.csrf.token_manager'
        - '@request_stack'
        - '@router'
  1. 会话配置优化:确保垃圾回收周期大于Cookie有效期,避免会话提前被清理:
# config/packages/framework.yaml
session:
    handler_id: 'session.handler.native_file'
    cookie_secure: true
    cookie_samesite: lax
    save_path: '%kernel.project_dir%/var/sessions/%kernel.environment%'
    cookie_lifetime: 20
    gc_maxlifetime: 60 # 必须大于cookie_lifetime
    gc_probability: 1
    gc_divisor: 100

关键注意点

  • 会话检测必须在REQUEST阶段执行,此时设置响应能直接终止后续逻辑,不会被覆盖。
  • 刷新Cookie时复用会话原有参数,避免出现Cookie不匹配导致的会话失效异常。
  • 始终过滤子请求,防止对AJAX、嵌入请求等误处理。

内容的提问来源于stack exchange,提问作者Jegi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 13:54:57