Asp.net Core 2部署Service Fabric多机集群时登录会话丢失问题
解决ASP.NET Core 2部署Service Fabric后会话丢失与"记住我"失效问题
兄弟,你的这个问题太典型了——本地单节点测试完全正常,一上多节点Service Fabric集群就频繁要求重登,连续几次后才暂时稳定,连"记住我"都失效,这绝对是分布式环境下的会话一致性和负载均衡会话亲和性的锅!下面给你拆解原因和靠谱的解决办法:
核心问题根源
- 默认内存会话不支持分布式:ASP.NET Core默认用内存存会话数据,每个Service Fabric节点的应用实例都有自己独立的内存池子。用户请求被负载均衡器分到不同节点时,新节点压根没有之前的会话信息,自然就会踢你重新登录。连续登录几次后暂时正常?那大概率是负载均衡器临时把你的请求粘到了同一个节点,但这完全是碰运气,根本不可靠。
- "记住我"的Cookie验证没做分布式兼容:如果
CookieAuthenticationOptions里的DataProtectionProvider没配置分布式密钥存储,每个节点的加密密钥都是独立的,其他节点根本解不开你登录时生成的"记住我"Cookie,直接就判定你没登录;另外如果Cookie的过期时间被误设成10分钟,那会话自然撑不了多久。 - 负载均衡器没开会话亲和:Azure负载均衡器或者Service Fabric反向代理默认可能没启用会话亲和,请求会被随机扔到不同节点,会话丢失就是必然的。
一步步解决问题
1. 换成分布式会话存储(重中之重)
ASP.NET Core的会话和身份验证都依赖IDistributedCache,必须把默认的内存实现换成分布式的,比如Redis或者Azure Cosmos DB,让所有节点共享同一套会话数据:
用Redis做分布式缓存(最常用方案)
先装NuGet包:
Install-Package Microsoft.Extensions.Caching.StackExchangeRedis
然后在Startup.cs的ConfigureServices里配置:
public void ConfigureServices(IServiceCollection services) { // 配置Redis缓存 services.AddStackExchangeRedisCache(options => { options.Configuration = "你的Redis连接字符串"; options.InstanceName = "AspNetCoreSession_"; // 给会话键加个前缀,避免和其他应用冲突 }); // 配置会话 services.AddSession(options => { options.IdleTimeout = TimeSpan.FromHours(2); // 按需设置会话超时时间 options.Cookie.HttpOnly = true; options.Cookie.IsEssential = true; options.Cookie.SameSite = SameSiteMode.Lax; }); // 配置Cookie认证 services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options => { options.LoginPath = "/Account/Login"; options.LogoutPath = "/Account/Logout"; options.ExpireTimeSpan = TimeSpan.FromDays(7); // "记住我"的有效期,按需调整 options.SlidingExpiration = true; // 关键:让所有节点共用同一套加密密钥,不然其他节点解不开Cookie // 可以用共享文件存储,或者Azure Blob/Redis来存密钥 options.DataProtectionProvider = DataProtectionProvider.Create(new DirectoryInfo(@"\\你的共享存储路径\keys")); // 如果用Azure,也可以用Blob存储: // options.DataProtectionProvider = DataProtectionProvider.Create(new AzureBlobStorageXmlRepository(new Uri("https://你的存储账户.blob.core.windows.net/keys/protection-keys"))); }); }
还要记得在Configure中间件里启用会话和认证:
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { // ...其他中间件(比如静态文件、错误页) app.UseSession(); // 必须在UseAuthentication之前 app.UseAuthentication(); app.UseMvc(); }
2. 给负载均衡器开会话亲和
如果你的应用是通过Azure负载均衡器或者Service Fabric反向代理对外暴露的,得开启会话亲和性,让同一客户端的请求尽量打到同一个节点:
- Azure负载均衡器:找到VMSS对应的负载均衡器,在负载均衡规则里把"会话亲和性"改成"客户端IP",这样同一IP的请求会被路由到同一个节点,直到会话超时或者负载均衡器重新分配。
- Service Fabric反向代理:如果用了反向代理,在应用的
ServiceManifest.xml里给端点加上SessionAffinityEnabled="true":
<Resources> <Endpoints> <Endpoint Name="ServiceEndpoint" Protocol="http" Port="80" SessionAffinityEnabled="true" /> </Endpoints> </Resources>
3. 检查"记住我"的代码实现
确保登录的时候正确传了IsPersistent = true:
// 构建用户身份 var claimsIdentity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme); claimsIdentity.AddClaim(new Claim(ClaimTypes.Name, "用户名")); // ...添加其他用户Claim // 登录,开启记住我 await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity), new AuthenticationProperties { IsPersistent = true, // 必须设为true才会启用记住我 ExpiresUtc = DateTimeOffset.UtcNow.AddDays(7) });
4. 确认节点时钟同步
虽然概率不高,但Service Fabric集群的所有VM节点必须时钟同步,不然Cookie的过期时间校验会出错,导致会话提前失效。Azure VMSS默认会启用时间同步,但还是可以手动检查一下,确保所有节点的时间一致。
总结
先搞定分布式会话存储和DataProtection密钥共享,这是多节点环境下会话稳定的核心。配好分布式缓存后,再加上负载均衡的会话亲和性,就能彻底解决频繁重登和"记住我"失效的问题。之前连续登录几次后暂时正常,只是负载均衡临时把你粘到同一节点的巧合,根本不是解决办法哦。
内容的提问来源于stack exchange,提问作者Francesco Cristallo
相关产品推荐
相关产品推荐

