为什么我的会话状态在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,一经查实,立即删除!

相关文章

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

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

Help Jimmy POJ - 1661

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

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

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

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技术社区首次线下聚会全程回顾【多图】

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

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

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

【学习笔记】信息学竞赛中的概率与期望小结

信息竞赛——概率与期望事件事件的蕴含、包含事件的互斥事件的对立事件的和&#xff08;并&#xff09;事件的积&#xff08;交&#xff09;事件的差概率事件的独立性全概率公式贝叶斯公式概率DP&#xff08;竞赛中的考察&#xff09;期望&#xff08;竞赛中的考察&#xff09;…

尝试:Script Lab,快速 O365 开发工具//SL01)

《前言》Script Lab 我希望有一个系列&#xff08;连载&#xff09;&#xff0c;可是我挺担心没偿没有能力去驾驭它。虽然早年前己经接触过&#xff0c;但一直未有下决心开始 Office 365 的开发之旅&#xff0c;虽然一直被光标老师所鼓舞&#xff0c;但是我心有旁骛还没有真正做…

Keiichi Tsuchiya the Drift King

Keiichi Tsuchiya the Drift King 题意&#xff1a; 给定一辆小车长宽分别为 b&#xff0c;a&#xff0c;轨道的圆弧部分半径为 r&#xff0c;圆弧对应的角度为 d&#xff0c;求出小车能通过轨道的最小轨道宽度 w。 题解&#xff1a; 我们考虑小车处于什么状态会使弯道最宽…

AspNet Core 下利用普罗米修斯+Grafana构建Metrics和服务器性能的监控

概述Prometheus是一套开源的监控&报警&时间序列数据库的组合,起始是由SoundCloud公司开发的。该项目有非常活跃的社区和开发人员&#xff0c;目前是独立的开源项目&#xff0c;现在最常见的Kubernetes容器管理系统中&#xff0c;通常也会搭配Prometheus进行监控。prome…

软件工程真的是一门什么用都没有的学科么?

软件工程真的是一门什么用都没有的学科么&#xff1f;-----读《构建之法》有感楔子我很惭愧&#xff0c;构建之法这本书已经出版四五年了&#xff0c;我之前却未曾涉猎&#xff0c;还是在通过组织长沙.net技术社区之后&#xff0c;才因为因缘际遇有幸认识邹欣邹老师之后&#x…

Ball Dropping

Ball Dropping 题意&#xff1a; 求&#xff1f;的具体长度 题解&#xff1a; 算一算就出来了 代码&#xff1a; #include<bits/stdc.h> using namespace std; int main(){double r,a,b,h;cin>>r>>a>>b>>h;if(2*r<b&&2*r<…

尝试:Script Lab,开发模式之知识储备//SL02

前期00&#xff1a;深度&#xff1a;从 Office 365 新图标来看微软背后的设计新理念前期01&#xff1a;尝试&#xff1a;Script Lab&#xff0c;快速 Office 365 开发工具 //SL01本期02&#xff1a;尝试&#xff1a;Script Lab&#xff0c;开发模式之知识储备 //SL02项目特点适…

WebApi网关之Bumblebee和Ocelot性能对比

Bumblebee是基于.net core 2.1开发的WebApi网关组件&#xff0c;由于Bumblebee所追求的轻量化和性能&#xff0c;所以它并没有像Ocelot那样从asp.net core上进行扩展&#xff1b;而是构建在BeetleX.FastHttpApi之上&#xff0c;主要原因BeetleX.FastHttpApi有着更轻量化和高性能…

在 .NET Core 中运行 JavaScript

一.前言在 .NET Framework 时&#xff0c;我们可以通过V8.NET等组件来运行 JavaScript&#xff0c;不过目前我看了好几个开源组件包括V8.NET都还不支持 .NET Core &#xff0c;我们如何在 .NET Core 中运行 JavaScript 呢&#xff0c;答案是使用 NodeServices。关于为何有在 .N…

Named Volume 在 MySQL 数据持久化上的基本应用

原文作者&#xff1a;春哥非常感谢春哥的投稿&#xff0c;同时也有一些感慨。初识春哥时&#xff0c;春哥是美术设计大咖。后不久&#xff0c;创业并致力于游戏开发&#xff0c;已有3年。从Unity3D到IOS&#xff08;Swift&#xff09;开发&#xff0c;从前端开发到后端以及容器…

.Netcore 2.0 Ocelot Api网关教程(7)- 限流

本文介绍Ocelot中的限流&#xff0c;限流允许Api网关控制一段时间内特定api的总访问次数。限流的使用非常简单&#xff0c;只需要添加配置即可。1、添加限流修改 configuration.json 配置文件&#xff0c;对 UpstreamPathTemplate 为 /webapib/values 的配置修改如下&#xff1…

微信开发必看,使用.Net Core 开发微信跨平台应用

.NET Core 是一个开源通用的开发框架&#xff0c;源码由微软官方和社区共同支持。支持跨平台&#xff0c;即支持在 Window&#xff0c;macOS&#xff0c;Linux 等系统上的开发和部署&#xff0c;并且可以在硬件设备&#xff0c;云服务&#xff0c;和嵌入式/物联网方案中进行使用…

CF1526 D. Kill Anton

CF1526 D. Kill Anton 题意&#xff1a; 给你一个由’A’,‘N’.‘T’,O’四个字符组成的字符串b&#xff0c;现在要求你改变b的顺序得到a&#xff0c;使得a通过移动回到b的步数最多。 每次移动只能移动相邻两项 题解&#xff1a; 官方题解说&#xff1a;最佳情况为相同字符…

ASP.NET Core 3.0预览版体验

目前.NET Core 3.0的版本为.NET Core 3.0 Preview 3&#xff0c;对应ASP.NET Core 3.0 Preview 3。ASP.NET Core 3.0 之后将不再支持.NET Framework&#xff0c;只运行在.NET Core 上面。ASP.NET Core 3.0 现在已经出到了第三个预览版&#xff0c;增加和改进了很多功能。环境准…