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

多用户访问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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 11:34:57