多用户访问Sulu CMF站点时用户上下文异常切换及环境配置咨询
问题描述
我按照Sulu的用户上下文缓存指南配置了网站的受限区域,登录、登出功能正常,但当多个用户访问时,用户上下文会突然切换。例如,以用户A登录后,点击几次页面,页面头部模板通过{{ app.user.username }}显示的用户名会突然变成用户B。线上环境采用Nginx托管静态内容、Apache托管动态内容,请问是否需要进行特殊配置?
相关配置文件
config/routes/fos_http_cache.yaml
user_context_hash: path: /_fos_user_context_hash
config/packages/fos_http_cache.yaml
fos_http_cache: proxy_client: symfony: use_kernel_dispatcher: true user_context: enabled: true role_provider: true hash_cache_ttl: 0
src/Kernel.php
<?php declare(strict_types=1); namespace App; /* * This file is part of Sulu. * * (c) Sulu GmbH * * This source file is subject to the MIT license that is bundled * with this source code in the file LICENSE. */ use FOS\HttpCache\SymfonyCache\HttpCacheProvider; use Sulu\Bundle\HttpCacheBundle\Cache\SuluHttpCache; use Sulu\Component\HttpKernel\SuluKernel; use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\HttpKernelInterface; class Kernel extends SuluKernel implements HttpCacheProvider { private ?HttpKernelInterface $httpCache = null; protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader): void { $container->setParameter('container.dumper.inline_class_loader', true); parent::configureContainer($container, $loader); } public function getHttpCache(): HttpKernelInterface { if (!$this->httpCache instanceof HttpKernelInterface) { $this->httpCache = new SuluHttpCache($this); // Activate the following for user based caching $this->httpCache->addSubscriber( new \FOS\HttpCache\SymfonyCache\UserContextListener([ 'session_name_prefix' => 'SULUSESSID', ]) ); } return $this->httpCache; } }
config/webspaces/webspace.xml
<?xml version="1.0" encoding="utf-8"?> <webspace xmlns="http://schemas.sulu.io/webspace/webspace" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://schemas.sulu.io/webspace/webspace http://schemas.sulu.io/webspace/webspace-1.1.xsd"> <!-- Configure your webspace as per Sulu docs --> <name>example.io</name> <key>example</key> <localizations> <localization language="en" default="true" /> <localization language="es" /> </localizations> <security permission-check="true"> <system>private_sale</system> </security> <default-templates> <default-template type="page">default</default-template> <default-template type="home">homepage</default-template> </default-templates> <templates> <template type="search">search/search</template> <template type="error">error/error</template> <template type="error-404">error/error-404</template> </templates> <navigation> <contexts> <context key="main"> <meta> <title lang="en">Main Navigation</title> <title lang="de">Hauptnavigation</title> </meta> </context> <context key="footer_quicklinks"> <meta> <title lang="en">Footer Quicklinks</title> <title lang="en">Footer Quicklinks</title> </meta> </context> <context key="footer_support"> <meta> <title lang="en">Footer Support</title> <title lang="en">Footer Support</title> </meta> </context> </contexts> </navigation> <portals> <portal> <name>example.io</name> <key>example</key> <environments> <environment type="prod"> <urls> <url>{host}/{localization}</url> </urls> </environment> <environment type="stage"> <urls> <url>{host}/{localization}</url> </urls> </environment> <environment type="test"> <urls> <url>{host}/{localization}</url> </urls> </environment> <environment type="dev"> <urls> <url>{host}/{localization}</url> </urls> </environment> </environments> </portal> </portals> </webspace>
解决方案
这个问题核心是多用户场景下缓存键未正确区分用户上下文,加上Nginx与Apache的代理配置未正确传递会话标识或用户上下文哈希,导致不同用户命中同一份缓存。
1. 调整Nginx代理配置
确保Nginx完整传递会话Cookie和用户上下文哈希头,且不对动态内容做缓存:
# 动态内容转发到Apache location ~ ^/(index\.php|_fos_user_context_hash) { proxy_pass http://your_apache_backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 传递会话Cookie和用户上下文哈希头 proxy_set_header Cookie $http_cookie; proxy_set_header X-User-Context-Hash $http_x_user_context_hash; # 禁止Nginx缓存动态请求 proxy_no_cache 1; proxy_cache_bypass 1; } # 静态内容缓存(若受限区域有静态资源,需按用户上下文哈希区分缓存) location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg)$ { expires 1y; add_header Cache-Control "public, immutable"; # 受限区域静态资源需添加哈希到缓存键 # proxy_cache_key "$scheme$request_uri$http_x_user_context_hash"; }
2. 配置Apache缓存规则
禁用受限区域和用户上下文哈希请求的缓存:
# 禁用受限区域页面缓存 <Location "/restricted-area"> CacheDisable on </Location> # 禁用用户上下文哈希接口的缓存 <Location "/_fos_user_context_hash"> CacheDisable on </Location>
3. 完善FOS HttpCache配置
确保用户上下文哈希基于用户ID和角色生成,避免不同用户生成相同哈希:
fos_http_cache: proxy_client: symfony: use_kernel_dispatcher: true user_context: enabled: true role_provider: true hash_cache_ttl: 0 # 启用安全和会话上下文提供者,确保哈希区分用户 context_providers: security: enabled: true session: enabled: true
4. 验证会话前缀配置
检查浏览器Cookie中的会话名称是否以SULUSESSID开头,若不符,修改Kernel.php中UserContextListener的session_name_prefix为实际会话前缀。
5. 受限页面添加私有缓存头
在受限页面模板中添加缓存控制头,明确页面为私有缓存,禁止公共缓存:
{# 受限页面模板中添加 #} {% do response.setHeader('Cache-Control', 'private, must-revalidate') %}
内容的提问来源于stack exchange,提问作者Mario A
相关产品推荐
相关产品推荐

