symfony session生命周期由framework.session配置决定,存储位置、gc策略、安全cookie等均需显式配置;security组件通过tokenstorage和sessionauthenticationstrategy管理认证状态,登出必须触发完整流程以同步清除token、session及remember-me令牌。

Session存储位置和生命周期由framework.session配置决定
Symfony 默认使用 PHP 原生 session(php://files),但实际行为取决于 framework.session 配置项。你不能只看是否调用了 $session->set() 就认为数据一定落盘或跨请求可用。
常见陷阱是:开发环境启用了 save_path: '%kernel.cache_dir%/sessions',而生产环境没配 handler_id,结果 session 文件权限不对、目录不可写,导致每次请求都新建 session ID —— 表现为“登录后立刻掉登”。
-
handler_id必须显式设置才能切换存储后端(如session.handler.pdo或 Redis);留空就走 PHP 默认,且不受 Symfony 控制 -
cookie_secure和cookie_samesite不仅影响安全,还直接影响前端能否携带 cookie:若前端是https://app.example.com,而后端 dev server 是http://localhost:8000,samesite: strict会让浏览器直接丢弃 cookie -
gc_maxlifetime要与 PHP ini 中的session.gc_maxlifetime一致,否则垃圾回收时间不匹配,会提前销毁活跃 session
Security 组件不直接读写 session,而是通过 TokenStorage 和 SessionAuthenticationStrategy
很多人误以为 $session->get('_security_main') 就是用户登录状态的唯一来源。其实 Symfony Security 在认证成功后,会把 PostAuthenticationToken 存入 TokenStorage,再由 SessionAuthenticationStrategy 决定是否写入 session、何时刷新、是否再生 ID。
典型问题:自定义 User 实体时重写了 getUserIdentifier() 返回 username,但 EntityUserProvider 仍按 id 加载用户,导致 session 中存的是 username,而刷新时却用 id 查询 —— 日志里出现 Cannot refresh token because user has changed。
- 确保
User::getUserIdentifier()返回的值,在UserProvider::loadUserByIdentifier()中能被准确查到(比如数据库字段名、大小写、NULL 处理) - 不要手动
$session->remove('_security_main')来“登出”,应调用LogoutEvent或Security::logout(),否则 TokenStorage 和 session 不同步 -
SessionAuthenticationStrategy默认是DefaultSessionAuthenticationStrategy,它会在用户变更时强制再生 session ID;若你做了“改邮箱不登出”,需自定义策略并禁用该行为
登出时必须清除 TokenStorage、session 和 remember-me 令牌
仅调用 $session->clear() 不足以完成登出:它清 session 数据,但 TokenStorage 中的 token 还在,后续中间件仍可能认为用户已认证;remember-me cookie 若未失效,下次访问又会自动登录。
最简可靠做法是触发完整 logout 流程:LogoutUrlGenerator::getLogoutUrl() 生成的 URL 会激活 LogoutListener,它按顺序执行:
- 调用所有注册的
LogoutSuccessHandlerInterface - 从
TokenStorage清除 token - 调用
SessionAuthenticationStrategy::onLogout()清 session key(如_security_main) - 若启用 remember-me,调用
RememberMeServicesInterface::loginFail()使 cookie 失效
手动清理时漏掉任一环,都会造成“看似登出,实则还能访问受保护路由”的现象。
多防火墙共存时,session key 和 token storage 是隔离的
当你配置了多个 firewall(如 main 用于 Web、api 用于 JWT),它们默认共享同一个 session,但各自维护独立的 TokenStorage 实例和 session key(如 _security_main vs _security_api)。这意味着:
- Web 用户登录后,
apifirewall 无法感知其身份,除非你显式配置stateless: false并复用同一 key - 若两个 firewall 都写
_security_main,后写的会覆盖前写的,导致其中一个始终掉登 - 使用
switch_user时,切换目标用户的 role 可能因 firewall scope 不同而加载失败 —— 因为SwitchUserEvent只作用于当前 active firewall
session 本身是全局的,但 Security 对它的使用方式是 per-firewall 的。别指望靠改 session 变量来绕过 firewall 边界。











