未设置 ASP.NET Core 会话 Cookie 的问题(间歇性)
我正在使用 ASP.NET Core 会话,由于某种原因,中间件没有始终如一地发送
Set-Cookie
cookie 的 .AspNetCore.Session
标头。这个问题变得更加奇怪,因为有时它工作得很好,但有时却不行。
例如,在 DevMachine1 上,它当前不起作用,但如果我转到 DevMachine2 并使用相同的中间件配置运行完全相同的存储库,它会按预期工作。
Program.cs
:builder.Services.AddHttpClient();
builder.Services.AddHttpContextAccessor();
builder.Services.AddControllersWithViews();
builder.Services.AddScoped<AutomateAPIService>();
builder.Services.AddScoped<AutomateDataService>();
// builder.Services.AddSingleton<PersistService>(); // For Future Persist Integration, removing to test sessions.
builder.Services.AddScoped<AutomateLoginService>();
builder.Services.AddTransient<JsonParserService>();
builder.Services.AddSingleton<IMongoClient, MongoClient>(sp => new MongoClient(connectionUri));
builder.Services.AddTransient<PersistService>();
builder.Services.AddTransient<SessionValidationService>();
builder.Services.AddScoped<SessionAuthorizationFilter>();
builder.Services.AddScoped<DeviceJsonParser>();
builder.Services.AddDistributedMemoryCache();
// MongoSessionStorage
builder.Services.AddSingleton(sp =>
{
var client = sp.GetRequiredService<IMongoClient>();
var database = client.GetDatabase("SessionDatabase");
return database.GetCollection<BsonDocument>("Sessions");
});
// Add Session
builder.Services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(60);
options.Cookie.SecurePolicy = CookieSecurePolicy.None;
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
options.Cookie.SameSite = SameSiteMode.Lax;
});
builder.Services.AddSingleton<ISessionStore, MongoDbSessionStore>();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseSession();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
您可能会注意到,会话存储在 MongoDB 数据库中。此设置以前曾有效,并且在某些机器上仍然间歇性地工作。 回滚到内存缓存也没有解决问题。
我们退出会话存储并看到以下内容:
Microsoft.AspNetCore.HttpsPolicy.HttpsRedirectionMiddleware[3]
Failed to determine the https port for redirect.
info: WebAppApiTest.Services.MongoDbSessionStore[0]
Setting SessionKey, System.Byte[] for session
info: WebAppApiTest.Controllers.HomeController[0]
SessionKey value: SessionValue
info: WebAppApiTest.Services.MongoDbSessionStore[0]
Attempting to commit session ca2084a4-23c1-f13f-4bb2-661251e14d38
info: WebAppApiTest.Services.MongoDbSessionStore[0]
Session ca2084a4-23c1-f13f-4bb2-661251e14d38 committed successfully. Matched: 0, modified 0, Upserted: True
我们可以看到会话 ID,因此我们知道中间件正在工作在一定程度上,但没有设置会话 cookie,这意味着我们的会话存储永远没有正确或持久的会话 ID 可供参考。
我尝试在我的
LoginController
上强制进行会话交互,看看是否可以让 ASP.NET 发送会话 cookie:
public IActionResult AutomateLogin()
{
HttpContext.Session.SetString("SessionKey", "SessionValue");
var sessionValue = HttpContext.Session.GetString("SessionKey");
_logger.LogInformation($"SessionKey value: {sessionValue}");
return View(new AutomateLoginModel());
}
日志确认会话交互正在发生:
info: WebAppApiTest.Services.MongoDbSessionStore[0]
Setting SessionKey, System.Byte[] for session
info: WebAppApiTest.Controllers.AutomateLoginController[0]
SessionKey value: SessionValue
info: WebAppApiTest.Services.MongoDbSessionStore[0]
Attempting to commit session 6207e6cb-6eeb-688e-0e37-c60e32f4a004
info: WebAppApiTest.Services.MongoDbSessionStore[0]
Session 6207e6cb-6eeb-688e-0e37-c60e32f4a004 committed successfully. Matched: 0, modified 0, Upserted: True
仍然,没有设置会话cookie。
.AspNetCore.Session
) 时,什么可能导致 ASP.NET Core 会话
间歇性失败?即使配置相同,为什么会在某些计算机上设置会话 cookie 而在其他计算机上则不设置?我确实在多个浏览器上验证了这一点,并确认 Set-Cookie 标头不存在。我确实找到了原因:
因此,在 SessionMiddleware 类中,我们评估是否应该在此处包含标头:
重要更新:
在 SessionMiddleware.cs(一个 ASP.NET 本机类)中
Set-Cookie 标头根据响应是否启动进行有条件评估。如果响应已经开始,我们就不能再使用 Set-Cookie 标头。
但是,运行一段时间的调试后,看起来即使我的响应 HasStarted value = false,我们也没有设置 _shouldEstablishSession = true。
会话中间件.cs
internal bool TryEstablishSession()
{
return (_shouldEstablishSession |= !_context.Response.HasStarted);
}
当我在返回线上设置断点时,它根本没有被击中,所以即使我的 Response.HasStarted 为 false,因此 _shouldbuiltSession 应该为 true
private static Task OnStartingCallback(object state)
{
var establisher = (SessionEstablisher)state;
if (establisher._shouldEstablishSession)
{
establisher.SetCookie();
}
return Task.CompletedTask;
}
因此,在此任务中执行时,我们的 _shouldEstablishSession 值为 false。不确定为什么它没有评估,但这肯定是不包含 Set-Cookie 标头的问题。可能是中间件管道问题,但不确定为什么它表现为间歇性问题。
TryEstablishSession
没有被调用。此方法对于设置会话 cookie 至关重要,应在创建会话时作为委托传递。自定义会话存储中的
Create
方法应如下所示:
public ISession Create(string sessionKey, TimeSpan idleTimeout, TimeSpan ioTimeout, Func<bool> tryEstablishSession, bool isNewSession)
{
return new MongoDbSession(sessionKey, _sessionCollection, idleTimeout, _logger, tryEstablishSession);
}
一旦 tryEstablishSession
委托被传递给
LoadAsync
和
CommitAsync
等会话操作,会话 cookie 就开始正确设置。
SessionMiddleware
类中的断点终于被命中,并且会话cookie包含在响应中。修复:
tryEstablishSession
委托应在创建会话时传递并在整个会话生命周期中使用。这确保底层中间件可以调用它,从而允许设置会话 cookie。虽然问题一开始是间歇性的,但此解决方案解决了问题,并且会话 cookie 现在已一致设置。