为什么我的会话状态在ASP.NET Core中不工作了?

原文:Why isn't my session state working in ASP.NET Core? Session state, GDPR, and non-essential cookies

作者:Andrew Lock

译文:https://www.cnblogs.com/lwqlun/p/10526380.html

译者:Lamond Lu

640?wx_fmt=jpeg

在本篇博客中,我将描述一个关于会话状态(Session State)的问题, 这个问题我已经被询问了好几次了。这个问题的场景如下:

  • 创建一个新的ASP.NET Core应用程序

  • 一个用户在会话状态中设置了一个字符串值,例如HttpContext.Session.SetString("theme", "Dark");

  • 在下一次请求中,尝试从会话中读取这个自字符串的值HttpContext.Session.GetString("theme");, 但是得到的结果却是null!

  • “额,这个愚蠢的框架不工作了”(╯°□°)╯︵ ┻━┻

这个问题的原因是ASP.NET Core 2.1中引入的GDPR功能与会话状态互相影响了。在本篇博客中,我将描述为什么你会看到这种行为,以及一些处理它的方法。

GDPR中ASP.NET Core 2.1中引入的一个特性,如果你使用NET Core 1.x或2.0版本,你将不会遇到这个问题。但是请记住,自2019年6月27起,1.x版本即将失去支持,2.0版本已经不受支持了,因此你应该考虑升级到2.1及以上版本。

说明:

  • 《通用数据保护条例》(General Data Protection Regulation,简称GDPR)为欧洲联盟的条例,前身是欧盟在1995年制定的《计算机数据保护法》。

  • 2018年5月25日,欧洲联盟出台《通用数据保护条例》。

ASP.NET Core中的会话状态

就像我前面所说的,如果你使用的是ASP.NET Core 2.0及以前的版本,你不会遇到这个问题。这里我将借助ASP.NET Core 2.0展示一下预期的行为,以便说明遇到这个问题的人期望的会话状态行为。然后我将在ASP.NET Core 2.2中创建等效的应用程序,并显示会话状态不再起作用了。

什么是会话状态?

会话状态是一种可以回溯到ASP.NET(非核心)的功能,你可以使用它为浏览站点的用户存储和检索服务器端的值。 会话状态经常在ASP.NET应用程序中广泛使用,但经常由于一些原因而出现问题,主要是性能和可伸缩性。

ASP.NET Core中的你应该把会话状态看作针对每用户的缓存。 从技术角度来看,ASP.NET Core中的会话状态的功能需要2个独立的部分来完成:

  • 一个Cookie。 用来指定每个用户的唯一ID(Session ID)

  • 一个分布式缓存。用来存储与每个用户唯一ID关联的数据项

在一般的情况下,我会尽量避免使用会话状态,使用会话状态可能会有很多陷阱,如果不注意,就会引起一起不必要的问题。例如:

  • 会话是针对每个浏览器的,而不是每个登录用户的

  • 会话结束的时候,应该删除会话Cookie,但可能不会

  • 如果会话中没有任何值,它将会被删除,并重新生成一个新的会话ID

  • 本文中即将描述的GDPR问题

这里我们讲解了什么是会话状态,以及其工作的原理。在下一节中,我将创建一个小程序,这个小程序会使用会话状态存储你访问过的页面,然后在首页上显示该列表。

在ASP.NET Core 2.0项目中使用会话状态

为了说明ASP.NET Core 2.0版本和2.1以上版本的行为变化,我将先创建一个ASP.NET Core 2.0项目,因为我的电脑上安装了许多.NET Core SDK, 这里我将使用2.0 SDK(版本号2.1.202)来构建一个2.0项目模板。

这里我们首先创建一个global.json, 将当前app目录的SDK版本固定为2.1.202版本。

dotnet new globaljson --sdk-version 2.1.202

然后使用dotnet new命令创建一个新的ASP.NET Core MVC 2.0应用程序

dotnet new mvc --framework netcoreapp2.0

会话状态默认情况下是没有启用的,所以这里你需要先添加必要的服务。我们修改Startup.cs文件ConfigureServices方法来添加会话服务。默认情况下,ASP.NET Core将使用内存来存储会话信息,这对于测试来说很友好,但是生产环境中可能就需要替换为其他方式。

public void ConfigureServices(IServiceCollection services)
{
   services.AddMvc();
   services.AddSession(); // add session
}

当然,只添加服务是没有用的,我们还需要在管道中注册会话中间件。只有注册在会话中间件之后的中间件才可以访问会话状态,所以你需要将会话中间件放在MVC中间件之前。

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
  // ...其他配置
  app.UseSession();
  app.UseMvc(routes =>
  {
      routes.MapRoute(
          name: "default",
          template: "{controller=Home}/{action=Index}/{id?}");
  });
}

对于这个简单的例子,我将使用会话密钥"actions"来存储并读取一个字符串类型的会话值,这个会话值中会保存你访问过的所有页面。当你在不同的页面间浏览时,我们会将你访问过的页面以分号分隔的形式保存在"actions"会话值中。现在我们更新HomeController的代码:

public class HomeController : Controller
{
   public IActionResult Index()
  {
       RecordInSession("Home");
       return View();
  }

   public IActionResult About()
  {
       RecordInSession("About");
       return View();
  }

   private void RecordInSession(string action)
  {
       var paths = HttpContext.Session.GetString("actions") ?? string.Empty;
       HttpContext.Session.SetString("actions", paths + ";" + action);
  }
}

注意:Session.GetString(key)Microsoft.AspNetCore.Http命名空间中的一个扩展方法。

最后,我们修改Index.cshtml页面的代码如下,在页面中显示当前"actions"的会话值

@using Microsoft.AspNetCore.Http
@{
  ViewData["Title"] = "Home Page";
}

<div>
  @Context.Session.GetString("actions")
</div>

如果你现在运行应用程序并浏览几次,你将看到会话页面访问历史列表的构建。 在下面的示例中,我访问了主页三次,关于页面两次:

640?wx_fmt=png

如果查看当前页面关联的Cookie信息,你就会看到一个名为.AspNetCore.Session的Cookie, 它的值就是一个加密会话ID, 如果你删除这个Cookie, 你将会看到"actions"的值被重置,页面访问历史列表丢失。

640?wx_fmt=png

这种会话状态的行为就是大部分人所期望的,所以这里没有问题。但是当你使用ASP.NET Core 2.1/2.2版本创建相同项目之后,情况就不一样了。

在ASP.NET Core 2.2项目中使用会话状态

为了创建ASP.NET Core 2.2应用程序,我使用了几乎相同的行为,但这次我没有固定SDK。 我安装了ASP.NET Core 2.2 SDK(2.2.102),因此以下命令会生成一个ASP.NET Core 2.2 MVC应用程序:

dotnet new mvc

这里你依然需要显示注册会话服务,并启用会话中间件,这一部分代码和前面一模一样。

与以前的版本相比,较新的2.2模板已经简化,因此为了保持一致性,我从2.0应用程序复制了HomeController。 我还复制了Index.chtml,About.chtml和Contact.cshtml视图文件。 最后,我更新了Layout.cshtml,为标题中的About和Contact页面添加了链接。

这2个应用程序,除了使用的ASP.NET Core版本不一样,其他的部分基本都是一样的。然而这次运行的时候,当你浏览一些页面之后,首页只会显示你访问过首页,而不会显示你访问过其他页面。

640?wx_fmt=png

不要点击隐私政策的横幅 - 后面你将马上知道原因


现在如果你去查看一下你的Cookies, 你会发现加密会话ID.AspNetCore.Session不存在。

640?wx_fmt=png


一切都显然配置正确,并且会话本身似乎也在工作(因为可以在Index.cshtml中成功检索HomeController.Index中设置的值)。 但当页面重新加载,或者在导航之间跳转的时候,没有保存会话状态。

那么为什么会话状态在ASP.NET Core 2.0中正常工作, 在ASP.NET Core 2.1/2.2中反而没有正常工作了呢?


到底发生了什么?GDPR

问题的原因,是因为ASP.NET Core 2.1版本之后,引入了一些新功能。为了帮助开发人员遵守2018年生效的GDPR规则,ASP.NET Core 2.1版本引入了一些扩展点,以及模板的更新。


针对这些新功能的官方文档写的都很详细,这里我只做简单总结:

  • 同意Cookie对话框 - 默认情况下,在用户点击同意对话框之前,ASP.NET Core不会将“非必要”的cookies写入响应中

  • Cookie可以被设置为必要或者非必要的 - 无论用户是否同意,必要的Cookies都会发送给浏览器,非必要的Cookies需要得到用户的同意

  • 会话Cookie被认为是非必要的 - 因此,在用户同意之前,无法跨导航或页面重新加载跟踪会话。

  • 临时数据(Temp Data)是非必要的 - ASP.NET Core 2.0以上版本中,临时数据提供器使用Cookie来存储数据项,所以在用户同意之前,临时数据功能是不可用的

所以问题是我们需要用户同意使用Cookie。 如果单击隐私横幅上的“Accept”,则ASP.NET Core可以编写会话cookie,并恢复预期的功能。

640?wx_fmt=png

如何在ASP.NET Core 2.1及以上版本中使用会话状态

根据你正在构建的程序,你可以使用多种选项。哪一个最适合你取决于你的使用场景,但是请注意,这些功能是为了帮助开发人员遵守GDPR而添加的。

如果你不在欧洲国家,或者你认为GDPR对自己没有什么影响,最好请阅读一下https://andrewlock.net/session-state-gdpr-and-non-essential-cookies/ - GDPR可能依然适用于你

这里主要的可选项如下:

  1. 在用户同意Cookie之前,接受该会话状态可能不可用。

  2. 在用户同意Cookie之前,禁用需要会话状态的功能。

  3. 禁用Cookie同意功能

  4. 将会话Cookie标记为必要的

我将在下面详细介绍每个选项,请记住考虑你的选择可能会影响你是否遵守GDPR!

接受当前的行为

“最简单”的选择就是接受现有的行为。 ASP.NET Core中的会话状态通常只应用于临时数据,因此你的应用程序需要能够处理会话状态不可用的情况。

这取决于你使用会话的目的,可能可以实现或可能不能实现,但这是使用现有模板的最简单方法,并且将你接触GDPR问题方面风险降到了最低。

禁用需要会话的功能

第二种选择和第一种选择类似,应为你需要保持现有的行为。区别在于第一种选项会将会话简单的视为缓存,因此你始终需要假设会话值是可以读取和保存的。而第二种选项略有不同,因为你需要明确知道系统中哪些部分是需要会话状态的,并在用户同意Cookie之前,禁用它们。

例如, 你可以需要一个会话状态保存当前页面选择的主题。如果用户没有同意Cookie, 那么你只需要隐藏主题选择的功能。只要用户同意,再将它显示出来。

这感觉就像是针对选择一的改进,因为它主要改善了用户体验。如果你不考虑哪些功能是需要会话的,用户可能会产生一些疑惑。例如,如果你使用选项一,用户在切换主题的时候,程序永远不会记住它们的选择,这就很让人沮丧。

禁用Cookie同意功能

如果你确定不需要Cookie同意功能,你也可以很容易的禁用它。 默认模板在Startup.ConfigureServices中显式启用了Cookie同意功能。

public void ConfigureServices(IServiceCollection services)
{
   services.Configure<CookiePolicyOptions>(options =>
  {
       options.CheckConsentNeeded = context => true;
       options.MinimumSameSitePolicy = SameSiteMode.None;
  });

   services.AddSession(); // added to enable session
   services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

这里CheckConsentNeeded属性是一个标记,它用于检查是否应将非必要的cookie写入响应。 如果函数返回true(如上所述,模板中的默认值),则跳过非必要的cookie。 将此更改为false并且会话状态将起作用,而不需要用户明确同意cookie。

标记会话Cookie是必要的

完全禁用cookie同意功能可能会对你的应用程序造成一定的负担。 如果是这种情况,你可以将会话cookie标记为必要。

services.AddSession的重载方法,允许你传入一个会话配置对象。你可以使用它设置会话的超时时间,以及自定义会话Cookie。为了将会话Cookie标记为必要的,我们需要显式配置IsEssential的值是true。

public void ConfigureServices(IServiceCollection services)
{
   services.Configure<CookiePolicyOptions>(options =>
  {
       options.CheckConsentNeeded = context => true;
       options.MinimumSameSitePolicy = SameSiteMode.None;
  });

   services.AddSession(opts =>
  {
       opts.Cookie.IsEssential = true;
  });
   services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

使用这种方法,虽然应用程序依然会显示Cookie同意横幅,并且在点击之前不会写入非必要的Cookie。 但会议状态将在用户同意Cookie之前立即生效,因为它被认为是必要的。

总结

在这篇文章中,我描述了一个曾经多次被问过问题。开发人员发现他们的会话状态没有正确保存。 这通常是由于ASP.NET Core 2.1中引入的Cookie同意和非必要cookie的GDPR功能引起的。

我展示了一个问题的实例,以及它在2.0 app和2.2 app之间的区别。 我描述了会话状态如何依赖于默认情况下被认为是非必要的会话Cookie,因此在用户同意Cookie之前不会写入响应。

最后,我描述了处理这种行为的四种方法:

  • 什么也不做,接受它

  • 禁用依赖会话状态的功能,直到同意为止

  • 取消同意要求

  • 标记会话Cookie为必要的Cookie。


哪种选择最适合你将取决于你正在构建的应用程序,以及你对GDPR和类似法规的认识。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/316761.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Jury Compromise POJ - 1015

Jury Compromise POJ - 1015 题意&#xff1a; 在遥远的国家佛罗布尼亚&#xff0c;嫌犯是否有罪&#xff0c;须由陪审团决定。陪审团是由法官从公众中挑选的。先随机挑选n个人作为陪审团的候选人&#xff0c;然后再从这n个人中选m人组成陪审团。选m人的办法是&#xff1a; 控…

叁仟柒佰万(mex+线段树+dp+前缀和优化+双指针+桶)

叁仟柒佰万problemsolutioncode(50’)code(90’)code(100’)problem 多组数据。 给定一个序列 aaa&#xff0c;你可以将它划分成任意多段&#xff0c;满足每一个段的 mex 相同。 求方案数&#xff0c;对 109710^971097 取模。 T≤10,n≤3e5T\le 10,n\le 3e5T≤10,n≤3e5。 …

模板:吉司机线段树

所谓吉司机线段树&#xff0c;就是吉司机种的线段树 &#xff08;逃&#xff09; 解析 之前也看过一些相关内容的博客&#xff0c;但是因标记乱七八糟&#xff0c;感觉过于高深而放弃了… 直到看到洛谷这篇博客&#xff0c;完全颠覆了我的认知。&#xff08;怎么感觉语气像营…

现身说法:实际业务出发分析百亿数据量下的多表查询优化

今天给大家带来的讨论主题是通过实战经验来对百亿数据量下的多表数据查询进行优化&#xff0c;俗话说的好&#xff0c;一切脱离业务的架构都是耍流氓&#xff0c;接下来我就整理一下今天早上微信群里石头哥给大家分享的百亿数据量多表查询架构以及优化思路。由于本文内容整理自…

Help Jimmy POJ - 1661

Help Jimmy POJ - 1661 题意&#xff1a; 场景中包括多个长度和高度各不相同的平台。地面是最低的平台&#xff0c;高度为零&#xff0c;长度无限。 Jimmy老鼠在时刻0从高于所有平台的某处开始下落&#xff0c;它的下落速度始终为1米/秒。当Jimmy落到某个平台上时&#xff0c…

[HDU 6643] Ridiculous Netizens(点分治+根号分治+dp)

HDU 6643 Ridiculous Netizens problem hdu6643 题目大意&#xff1a;给定一棵无根树&#xff0c;以及每个点的点权 wiw_iwi​。 定义一个连通块的价值为连通块内点的点权之积。 求有多少个连通块价值 ≤m\le m≤m。 n≤2e3,m≤1e6n\le 2e3,m\le 1e6n≤2e3,m≤1e6。 solu…

SP422 TRANSP2 - Transposing is Even More Fun(Burnside引理,莫比乌斯反演)

解析 很巧妙的题。 关键是要利用好边长为2的整数次幂的性质。 对下标从1开始党极不友好。 首先显然答案就是 2ab2^{ab}2ab -环。 让下标均从0开始。 对于一个点 (i,j)(i,j)(i,j)&#xff0c;它原来的内存地址为 i∗2aji*2^aji∗2aj&#xff0c;转置后的地址为 j∗2bij*2^bij∗…

Fireworks(2020 ICPC南京)

Fireworks 题意&#xff1a; 你每做一个烟花要n分钟&#xff0c;释放已做好的所有烟花需要m分钟&#xff0c;每只烟花成功释放的概率为p。问你在采取最优策略的前提下&#xff0c;直到成功释放第一个烟花时最小的期望时间花费。 题解&#xff1a; 最佳策略是&#xff1a;每…

ASP.NET Core 沉思录 - ServiceProvider 的二度出生

ASP.NET Core 终于将几乎所有的对象创建工作都和依赖注入框架集成了起来。并对大部分的日常工作进行了抽象。使得整个框架扩展更加方便。各个部分的集成也更加容易。今天我们要思考的部分仍然是从一段每一个工程中都大同小异的代码开始的。IWebHostBuilder CreateWebHostBuilde…

[SPOJ - FTOUR2] Free tour II(点分治 + 背包dp + 启发式合并)

SPOJ - FTOUR2 Free tour II problem 给定一棵树&#xff0c;以及 mmm 个拥挤城市编号&#xff0c;选择一条最多包含 kkk 个拥挤城市的简单路径。 每条边有一个有趣度 www&#xff0c;可正可负。简单路径的价值定义为包含边的有趣度之和。 求最大价值。n≤2e5,∣w∣≤1e4n\…

PKUSC2022 游记

前言 1001822698014095235。 题的难度感觉比去年难不少&#xff0c;主要的体现在于两天的T1都没有之前那么可做了&#xff0c;在信息差的影响下几乎成了两场崩盘场。 由于比赛时间变短的原因&#xff0c;模拟反而比历年简单不少。 遗憾&#xff1a; d1t3的网络流莫名其妙无法…

Acwing 216. Rainbow的信号

Acwing 216. Rainbow的信号 题意&#xff1a; 给你n个数&#xff0c;在这n个数中&#xff0c;等概率地选取两个数l&#xff0c;r&#xff0c;如果l>r,则交换l,r 把信号中的第 l 个数到第 r 个数取出来&#xff0c;构成一个数列 P。 A 部分对话的密码是数列 P 的 xor 和的…

微软开源故事 | 开启 .NET 开源革命

如今&#xff0c;在微软构建开源软件是很正常的一件事——但早在2007年&#xff0c;我开始在微软工作时&#xff0c;情况并非如此。我们花了几年的时间才找到正确的方法&#xff0c;顺利开启了微软的开源之路。但是&#xff0c;如今我们已取得胜利&#xff0c;可以面带微笑地回…

【无码专区9】序列统计(带权并查集 + 前缀和建边 + dp)

因为只有std&#xff0c;没有自我实现&#xff0c;所以是无码专区 主要是为了训练思维能力 solution才是dls正解&#xff0c;但是因为只有潦草几句&#xff0c;所以大部分会有我自己基于正解上面的算法实现过程&#xff0c;可能选择的算法跟std中dls的实现不太一样。 std可能…

Acwing 217. 绿豆蛙的归宿

Acwing 217. 绿豆蛙的归宿 题意&#xff1a; 给出一个有向无环的连通图&#xff0c;起点为 1&#xff0c;终点为 N&#xff0c;每条边都有一个长度。 数据保证从起点出发能够到达图中所有的点&#xff0c;图中所有的点也都能够到达终点。 绿豆蛙从起点出发&#xff0c;走向…

LNOI2022:游记

前言 The world is cruel. 真的为身边的一些人感到可惜… Day -1 PKUSC刚刚考完&#xff0c;然后就要省选了&#xff1f; 板子实在是看的够够的了。 然而还是不想深度做题&#xff0c;看了看APIO的practise&#xff0c;T1写完发现偶数还得特别做&#xff0c;就感觉很麻烦&…

合肥.NET技术社区首次线下聚会全程回顾【多图】

2019年3月16日对于合肥.NET来说是一个特别的日子&#xff0c;因为这是合肥.NET技术社区首次非正式线下聚会&#xff01;这次聚会受场地限制&#xff08;毕竟是聚餐的形式&#xff09;&#xff0c;即使换成了小椅子后&#xff0c;最多也只能容纳24个人&#xff0c;所以还有一些小…

【无码专区10】第K大查询(双向链表 /主席树+st表)

已自我实现&#xff0c;但还是归入无码专区序列。哈哈哈哈哈 对于my idea部分&#xff0c;我的每一个想法都实现了&#xff0c;可供参考。 problem 给定一个 1∼n1\sim n1∼n 的排列和 kkk&#xff0c;求所有 r−l1≥kr-l1\ge kr−l1≥k 的区间 [l,r][l,r][l,r] 中的第 kkk 大…

SignalR第一节-在5分钟内完成通信连接和消息发送

前言首先声明&#xff0c;这又是一个小白从入门到进阶系列。 SignalR 这个项目我关注了很长时间&#xff0c;中间好像还看到过微软即将放弃该项目的消息&#xff0c;然后我也就没有持续关注了&#xff0c;目前的我项目中使用的是自己搭建的 WebSocket &#xff0c;连接管理和消…

P5327 [ZJOI2019]语言(线段树合并、生成树)

解析 只会扫描线树剖的三只log&#xff08;悲 考虑对每个 uuu 考虑合法的 vvv 的集合&#xff0c;必然是一个联通块。 进一步的&#xff0c;观察到这个联通块就是由所有经过 uuu 的路径的端点形成的最小生成树。 我们有一个最小生成树的经典结论&#xff1a;最小生成树边权和…