1、了解session

首先了解一下什么是会话,会话是指浏览器打开到关闭的过程中,多次与服务器发送接收数据的过程。 由于HTTP是无状态协议,一次请求响应过后,产生的数据就随之释放了,可是在某些情况下,我们希望服务器保存我们的一些数据,方便下次请求(比如网站的账户登录信息,等等)。如果要保存这些发送中的数据,就要用到会话技术(Cookie技术本节不涉及),服务器会将每个浏览器的单独标识,将每个浏览器需要保存的数据,保存下来,当下次需要这些保存的数据,就可以取出来用。 正式点说,会话技术(Session)服务器端保存浏览器请求数据的一项技术,数据是以键值对的形式保存到服务器内存中,可以解决无状态协议带来的弊端,减少每次请求的数据量,提高了性能。

2、.Net Core配置

接下来,了解一下,如何在ASP.NetCore中配置使用会话技术

2.1 startup

首先需要先配置一下,在startup文件中配置一下Session服务,然后添加Session中间件,需添加在路由中间件之前:
  1. //启用内存缓存(该步骤需在AddSession()调用前使用)
  2. services.AddDistributedMemoryCache();
  3. services.AddSession(options =>
  4. {
  5. options.Cookie.Name = ".AdventureWorks.Session";
  6. options.IdleTimeout = TimeSpan.FromSeconds(2000);//设置session的过期时间
  7. options.Cookie.HttpOnly = true;//设置在浏览器不能通过js获得该cookie的值
  8. });
此处介绍一下为什么要添加分布式内存缓存,官方文档的原文是:启用会话中间件,第一步是:添加任何IDistributedCache内存缓存。IDistributedCache实现用作会话后备存储,这能够保证会话数据的持久和稳定性,以及提高会话的性能,因为会话不像cookie,它的生命周期还是比较长的。

2.2 添加中间件

  1. app.UseHttpsRedirection();
  2. app.UseStaticFiles();
  3. app.UseSession();//添加会话中间件
  4. app.UseRouting();//路由中间件
  5. app.UseAuthorization();

3、使用

配置好了以后,通过Http上下文对象就可以使用了。要引用如下命名空间,需要其中的Session对象的支持。
  1. using Microsoft.AspNetCore.Http;
  2. HttpContext.Session.方法();//使用会话
写入数据的方法有三种:

.NET Core | 使用Session - 图1

Set是写入byte[] ,后两者见名知意,对应的获取数据的方法也有三种

.NET Core | 使用Session - 图2

3.1 在MVC Controller里使用HttpContext.Session

从nuget安装Microsoft.AspNetCore.Mvc引用,直接使用自带的方法进行设置和获取session。不过自带的方法设置和获取的session值是byte[]类型的,可以从nuget安装并引用Microsoft.AspNetCore.Http并使用里面的扩展方法。
  1. public class HomeController : Controller
  2. {
  3. public IActionResult Index()
  4. {
  5. HttpContext.Session.SetString("code", "123456");
  6. return View();
  7. }
  8. public IActionResult About()
  9. {
  10. ViewBag.Code = HttpContext.Session.GetString("code");
  11. return View();
  12. }
  13. }

3.2 如果不是在Controller里,你可以注入IHttpContextAccessor

  1. public class SessionTestClass
  2. {
  3. private readonly IHttpContextAccessor _httpContextAccessor;
  4. private ISession _session => _httpContextAccessor.HttpContext.Session;
  5. public SomeOtherClass(IHttpContextAccessor httpContextAccessor)
  6. {
  7. _httpContextAccessor = httpContextAccessor;
  8. }
  9. public void Set()
  10. {
  11. _session.SetString("code", "123456");
  12. }
  13. public void Get()
  14. {
  15. string code = _session.GetString("code");
  16. }
  17. }

3.3 Isession的扩展 存储复杂对象

  1. public static class SessionExtensions
  2. {
  3. public static void SetObjectAsJson(this ISession session, string key, object value)
  4. {
  5. session.SetString(key, JsonConvert.SerializeObject(value));
  6. }
  7. public static T GetObjectFromJson<T>(this ISession session, string key)
  8. {
  9. var value = session.GetString(key);
  10. return value == null ? default(T) : JsonConvert.DeserializeObject<T>(value);
  11. }
  12. }
使用范例:
  1. var myTestObject = new MyTestClass();
  2. HttpContext.Session.SetObjectAsJson("SessionTest", myTestObject);
  3. var myComplexObject = HttpContext.Session.GetObjectFromJson<MyClass>("SessionTest");

4、前后端分离开发跨controller获取不到session

理清思路:前后台分离项目session不能获取到的原因是因为跨域导致请求无法携带和服务器对应的cookie,不是因为前后台分离!前后台分离项目涉及跨域,但是通过一些手段可以避免跨域如nginx反向代理代理到同一个域下,其他方式如CORS过滤跨域都可以让ajax带上cookie,既然能带上cookie那就可以在前后台使用session了吧。

4.1 简单介绍

登录是一个网站最基础的功能。有人说它很简单,其实不然,登录逻辑很简单,但涉及知识点比较多,如:

密码加密、cookie、session、token、JWT等。

我们看一下传统的做法,前后端统一在一个服务中:

.NET Core | 使用Session - 图3

如图所示,逻辑处理和页面放在一个服务中,用户输入用户名、密码后,后台服务在session中设置登录状态,和用户的一些基本信息,

然后将响应(Response)返回到浏览器(Browser),并设置Cookie。下次用户在这个浏览器(Browser)中,再次

访问服务时,请求中会带上这个Cookie,服务端根据这个Cookie就能找到对应的session,从session中取得用户的信息,从而

维持了用户的登录状态。这种机制被称作Cookie-Session机制。

近几年,随着前后端分离的流行,我们的项目结构也发生了变化,如下图:

.NET Core | 使用Session - 图4

我们访问一个网站时,先去请求静态服务,拿到页面后,再异步去后台请求数据,最后渲染成我们看到的带有数据的网站。在这种结构下,

我们的登录状态怎么维持呢?上面的Cookie-Session机制还适不适用?

这里又分两种情况,服务A和服务B在同一域下,服务A和服务B在不同域下。在详细介绍之前,我们先普及一下浏览器的同源策略。

同源策略

同源策略是浏览器保证安全的基础,它的含义是指,A网页设置的 Cookie,B网页不能打开,除非这两个网页同源。

所谓同源是指:

  • 协议相同
  • 域名相同
  • 端口相同

例如:http://www.a.com/login,协议是http,域名是www.a.com,端口是80。只要这3个相同,我们就可以在请求(Request)时带上Cookie,

在响应(Response)时设置Cookie。

4.2 方案一

具体解决方案请移步:https://blog.csdn.net/bibiboyx/article/details/98034391

4.3 方案二

最近做前后端分离项目(.net core web api +vue)时,后台跨控制器不能获取到session。由于配置的是共享的session。本来以为是共享session出了问题,就在共享session这块找了大半天的问题,把相关组件删了一遍,到最后还是不行。最终转换了思路,是不是前后端分离的问题?是由前端引起的?一查资料,果然是前后端分离引起的问题。由于前台使用axios发送相应的请求,这里需要进行额外的配置

一. 在请求的全局配置中添加如下配置

  1. axios.defaults.withCredentials=true;
这段配置的主要功效是将header中的Access-Control-Allow-Credentials设置为true,意为开启cookie的认证请求。这个属性默认是关闭的 完成以上的配置问题大概也就解决了,于是再启动一波

.NET Core | 使用Session - 图5

天不随人愿,浏览器竟然报跨域的错。本来配置的跨域是没问题的,加上axios的这个配置就成这样了。。。

二. 后端跨域之前的配置

  1. services.AddCors(options =>
  2. {
  3. options.AddPolicy("all", builder =>
  4. {
  5. builder.AllowAnyOrigin() //允许任何来源的主机访问
  6. .AllowAnyMethod()
  7. .AllowAnyHeader();
  8. //.AllowCredentials();//指定处理cookie
  9. });
  10. });
之前的配置是这样的,由于.net core新版本出来之后,AllowAnyOrigin()和AllowCredentials()不能一起使用,于是就懒省事的把AllowCredentials()给注释了(自个给自个埋了个坑。。。)导致请求报跨域的错

三. 后端跨域最终配置

由于AllowAnyOrigin()和AllowCredentials()不能同时使用,我们便使用WithOrigins()来限制指定的主机访问
  1. services.AddCors(options =>
  2. {
  3. options.AddPolicy("all", builder =>
  4. {
  5. builder.WithOrigins("http://localhost:9000") //允许任何来源的主机访问
  6. .AllowAnyMethod()
  7. .AllowAnyHeader()
  8. .AllowCredentials();//指定处理cookie
  9. });
  10. });