ASP.NET MVC多端访问下Session存储的用户身份互相覆盖问题排查
核心异常本质
两个不同设备、不同域账号的请求,在服务端拿到了完全相同的Session实例,导致后一个请求写入的Session值直接覆盖了前一个请求的存储内容,本质是会话隔离机制失效,不是Session本身的读写逻辑问题。
逐点排查修复
核对Web.config会话配置
打开项目根目录Web.config,检查system.web节点下的sessionState配置,确保没有被错误修改:
<system.web> <sessionState mode="InProc" timeout="300" cookieless="UseCookies" cookieName="ASP.NET_SessionId" cookieSameSite="Lax" /> </system.web>重点排查3个常见错配:
- 不要将sessionState的mode设置为未做请求隔离的自定义存储提供程序,默认InProc模式是按SessionID隔离每个浏览器会话的
- 不要随意修改cookieName值为同域下其他站点使用的会话Cookie名,否则同域内不同路径的站点会共享同一个会话Cookie,直接出现会话串用
- 不要将cookieless设置为UseDeviceProfile这类基于URL存储SessionID的模式,这类模式容易因URL透传、缓存导致不同用户共享同一个SessionID
排查全局静态上下文串值
你封装的静态
tUser属性本身逻辑没有问题,但要排查项目中是否存在将HttpContext.Current存入全局静态变量的错误写法:反例:在静态类中定义
public static HttpContext GlobalContext;,并在请求管道中直接给这个静态属性赋值,没有做线程、请求维度的隔离
这类代码会导致不同请求的HttpContext实例互相覆盖,所有基于HttpContext读取的Session、用户身份、请求参数都会出现跨用户串值。修正OutputCache配置
你当前使用的
[OutputCache(NoStore = true, Duration = 60, VaryByParam = "*")]特性,默认不会按用户身份、会话维度区分缓存内容,即使设置了NoStore,遇到IIS内核输出缓存命中的场景,会把最后一个请求生成的响应返回给所有访问用户。
调整后的正确写法:[HttpGet] [OutputCache(NoStore = true, Duration = 0, VaryByParam = "*", VaryByCustom = "user")] public ActionResult Index() { System.Web.HttpContext.Current.Session["User"] = HttpContext.User.Identity.Name.ToString(); System.Web.HttpContext.Current.Session.Timeout = 300; var pModel = new PModel { Countries = PopulateDropDown(" SELECT ... ") }; return View(pModel); }同时在Global.asax中重写
GetVaryByCustomString方法,按会话ID、登录用户生成唯一的缓存键,确保不同用户的响应完全隔离:public override string GetVaryByCustomString(HttpContext context, string custom) { if (custom.Equals("user", StringComparison.OrdinalIgnoreCase)) { // 用当前会话ID+登录用户名生成唯一缓存键,杜绝跨用户缓存命中 return $"{context.Session.SessionID}_{context.User.Identity.Name}"; } return base.GetVaryByCustomString(context, custom); }核对IIS应用程序池配置
确保当前Web应用部署在独立的专属应用程序池下,没有和同服务器上的其他ASP.NET Web应用共用同一个应用程序池。多个应用共用同一应用池时,如果应用域隔离失效,会出现会话、应用级数据跨应用共享的问题,直接表现为不同用户的Session值被互相覆盖。
快速验证方法
在Index动作的最开头添加调试日志,打印每个请求的核心标识:
// 输出到调试窗口的日志,无侵入 System.Diagnostics.Debug.WriteLine( $"请求IP:{Request.UserHostAddress}, " + $"登录用户:{HttpContext.User.Identity.Name}, " + $"当前SessionID:{Session.SessionID}, " + $"Session中存储的User值:{Session["User"]}");
用两个不同设备分别访问时查看日志输出:
- 如果两个请求的SessionID完全相同,说明是会话Cookie配置错误,没有按设备隔离
- 如果SessionID不同但Session值被覆盖,说明是会话存储层、缓存层的隔离机制失效,按上面的步骤调整配置即可
内容的提问来源于stack exchange,提问作者Hamamelis

