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

.NET 7.0单页应用生产环境Session混淆问题排查求助

问题背景与现象

我开发了一个基于.NET 7.0的单页Web应用(核心为Index页面),包含多个局部视图,流程如下:

  • 首次带参数调用Index时,先清除Session,控制器及服务从数据库加载数据并存入Session,再将ID返回客户端;
  • 后续各局部视图通过Ajax回调服务器(部分带额外数据),从Session获取数据后调用服务和数据库获取更多信息,部分局部视图在页面加载时调用,部分在用户操作后触发。

该应用在本地及少量用户的测试服务器上运行正常,但在100人规模的生产环境中,偶尔会出现Session混淆问题:用户的局部视图(多为初始加载的视图)加载到其他用户的旧Session数据,每日发生数次。

注:仅在首次Index请求时(清除Session后)写入Session变量。

相关代码片段(program.cs)
try
{
    var builder = WebApplication.CreateBuilder(args);
    builder.Host.UseSerilog();

    builder.Services.Configure<AppSettings>(configuration);

    builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
                    .AddNegotiate()
                    .AddCookie();

    builder.Services.AddAuthorization(options =>
    {
        // By default, all incoming requests will be authorized according to the default policy.
        options.FallbackPolicy = options.DefaultPolicy;
    });

    builder.Services.AddControllersWithViews();

    builder.Services.AddRazorPages();

    builder.Services.......
    builder.Services.......
    builder.Services.......

    builder.Services.AddHttpContextAccessor();

    builder.Services.AddDistributedMemoryCache();

    builder.Services.AddSession(options =>
    {
        options.IdleTimeout = TimeSpan.FromSeconds(3600);
        options.Cookie.HttpOnly = true;
        options.Cookie.IsEssential = true;
        options.Cookie.SameSite = SameSiteMode.None;
        options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
        options.Cookie.Name = "DiscoursClient.Session";
    });

    var app = builder.Build();

    app.UseSerilogRequestLogging();
    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseAuthentication();
    app.UseAuthorization();

    app.UseSession();

    app.Use(async (context, next) =>
    {
        if (context.Request.Path == "/newcom/Index")
        {
            context.Session.Clear();
        }

        await next();
    });

    app.UseRouting();
    app.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");

    app.Run();
}
catch (Exception ex)
{
    Log.Fatal(ex, "Application terminated unexpectedly");
}
finally
{
    Log.CloseAndFlush();
}

另外,出现问题的局部视图通过@Html.AjaxGrid调用(使用了第三方Grid包)。

疑问
  1. 可能的原因是什么?
  2. 我对架构的理解是否存在错误?

问题分析与可能原因

1. 中间件顺序错误

当前中间件顺序存在致命问题:app.UseSession()的位置以及自定义清除Session的中间件顺序不符合ASP.NET Core的规范。正确的中间件执行顺序要求:

  • UseSession必须放在UseRouting之后、端点映射之前;
  • 自定义操作Session的中间件必须在UseSession之后执行,否则Session尚未初始化,context.Session.Clear()的操作无法生效,导致旧Session数据残留。

你现在的顺序会导致请求进入自定义中间件时,Session还未被激活,清除操作无效,后续请求复用了未被正确清理的Session容器。

2. 分布式内存缓存的局限性

你使用了AddDistributedMemoryCache(),这种缓存是进程内的:

  • 如果生产环境是多服务器/多进程部署,且未启用粘性会话,用户请求会被分发到不同服务器,Session数据无法跨进程共享,容易出现数据混淆;
  • 即使是单服务器,高并发场景下也可能出现Session ID复用或HttpContext上下文被错误共享的问题(比如服务中错误地将HttpContext捕获到静态变量中)。

3. 并发请求的时序问题

初始加载的局部视图会在页面加载时自动发起Ajax请求,可能出现多个请求并发到达服务器的情况:用户首次访问Index的清除Session请求还未完成,后续Ajax请求已经发送,此时Session可能还未完成初始化或清理,导致请求复用了旧的Session数据。

4. Session清除逻辑的缺陷

当前的清除逻辑仅通过路径匹配判断是否清除Session,存在漏洞:

  • 如果请求路径存在大小写差异、额外查询参数,会导致匹配失败,Session未被清除;
  • 清除Session后未主动生成新的Session ID,客户端仍持有旧ID,后续请求可能复用旧的Session容器,读取到其他用户的数据。
架构层面的问题

你依赖Session存储用户核心数据的设计在高并发、分布式环境下存在先天缺陷:

  • Session依赖Cookie传递Session ID,一旦Cookie被劫持或Session ID复用,就会导致数据混淆;
  • 分布式内存缓存不适合生产环境的Session存储,应替换为Redis、SQL Server等真正的分布式缓存,确保Session数据跨服务器共享;
  • 单页应用依赖Session缓存用户数据的设计不够健壮,建议将用户标识(如用户ID)存储在认证Cookie中,每次请求通过用户ID直接从数据库获取数据,彻底避免Session混淆问题,同时提升应用的可扩展性。

内容的提问来源于stack exchange,提问作者a.reure

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 16:40:17