ASP.NET Core 2.1 会话
ASP.NET Core 2.1 会话
在ASP.NET Core 2.1中,我无法访问会话变量。
在调试过程中,我注意到每个请求中会话ID都会发生变化(HttpContex.Session.Id)。
我在会话配置中犯了错误吗?
Startup.cs
public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.Configure(options => { // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; }); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); // Adds a default in-memory implementation of IDistributedCache. services.AddDistributedMemoryCache(); services.AddSession(options => { // Set a short timeout for easy testing. options.IdleTimeout = TimeSpan.FromSeconds(1000); options.Cookie.HttpOnly = true; }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.UseSession(); if (env.IsDevelopment()) { app.UseBrowserLink(); app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseCookiePolicy(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); } }
Program.cs
public class Program { public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup(); }
在调试过程中,我注意到每个请求的会话ID都会发生变化(HttpContex.Session.Id)。
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using ucms6.Models; namespace ucms6.Controllers { public class HomeController : Controller { const string SessionKeyName = "_Name"; const string SessionKeyYearsMember = "_YearsMember"; const string SessionKeyDate = "_Date"; public IActionResult Index() { // Requires using Microsoft.AspNetCore.Http; HttpContext.Session.SetString(SessionKeyName, "Rick"); HttpContext.Session.SetInt32(SessionKeyYearsMember, 3); return RedirectToAction("SessionNameYears"); } public IActionResult SessionNameYears() { var name = HttpContext.Session.GetString(SessionKeyName); var yearsMember = HttpContext.Session.GetInt32(SessionKeyYearsMember); return Content($"Name: \"{name}\", Membership years: \"{yearsMember}\""); } public IActionResult About() { ViewData["Message"] = "Your application description page."; return View(); } } }
问题的原因是ASP.NET Core 2.1中的会话(session)功能默认被视为非必要的cookie。根据GDPR的规定,非必要的cookie需要经过用户同意才能使用。因此,如果网站使用了会话功能但没有处理会话cookie的必要性,就可能导致GDPR合规性问题。
解决方法是将会话cookie标记为必要的。在代码中,可以通过设置opt.Cookie.IsEssential = true;
来实现。这样做可以维持cookie策略选项的完整性,并且会话功能仍然按预期工作,因为CookiePolicyOptions.CheckConsentNeeded
只对非必要的cookie生效。
这样做是否符合GDPR合规性要求?是的,只要不在未经同意的情况下跟踪用户并且不保存任何个人化数据,使用会话功能是没有问题的。
ASP.NET Core 2.1中出现session ID在控制器动作之间更改的问题是由于默认的分布式缓存存储方式为内存导致的。由于会话使用分布式缓存,所以会话存储也是在内存中。在内存中存储的内容与进程绑定,因此如果进程终止,存储在内存中的所有内容也会随之丢失。最后,当停止调试时,应用程序进程也会终止。这意味着每次开始和停止调试时,都会有一个全新的会话存储。
解决方法有两个途径。首先,如果只想运行网站而不进行调试,可以使用CTRL+F5。这将启动IIS Express并加载Web应用程序,而不会启动调试机制。然后,您可以继续发出尽可能多的请求,它们都将命中同一个进程(这意味着您的会话存储将保持完好)。这对于进行前端开发非常有用,因为您可以修改Razor视图、CSS、JS等,并查看这些更改,而无需停止和重新启动调试。但是,如果进行任何C#代码更改(类、控制器等),Visual Studio将启动构建过程,这将终止应用程序然后重新启动。您的站点将继续运行,就好像什么都没有发生过,但是存储在内存中的任何内容,包括会话,都将消失。尽管如此,这仍然比不断进行调试要好。
其次,在开发中可以使用持久存储,就像在生产中一样(如果尚未设置使用持久存储,请尽快进行修复)。可以在开发中使用类似SQL Server或Redis的持久存储。SQL存储可以添加到现有的开发数据库中,因此实际上不需要安装SQL Server。您还可以安装本地的Redis副本,并在本地主机上运行它,如果您更喜欢这种方式。无论哪种方法,您的分布式缓存和会话将存储在应用程序之外的某个外部存储中,因此启动和停止应用程序对其中存储的内容没有任何影响。
感谢您的回复。上面的代码来自Microsoft文档(除了startup.cs中的ConfigureServices和Configure)。所以它应该能够无缝工作,但实际上并没有。我注意到即使在单个请求中,会话ID在控制器动作之间也会更改。在上面的HomeController中,在重定向到SessionNameYears动作后,会话ID会更改。您有什么想法吗?
在Startup
类的ConfigureServices()
方法中,设置options.CheckConsentNeeded = context => false;
如下所示:
services.Configure<CookiePolicyOptions>(options => { options.CheckConsentNeeded = context => false; // 默认为true,将其设置为false options.MinimumSameSitePolicy = SameSiteMode.None; });
谢谢,这样就可以了,但是为什么默认代码是true?有什么作用吗?
我猜CheckConsentNeeded
默认为true
是为了符合GDPR的规定。如果您在页面顶部的同意弹窗中点击了接受(在默认模板中),您的会话cookie将按照您的期望开始工作。GDPR规定了cookie的操作方式,在用户同意使用它们之前将不会使用。