1、了解session
首先了解一下什么是会话,会话是指浏览器打开到关闭的过程中,多次与服务器发送接收数据的过程。 由于HTTP是无状态协议,一次请求响应过后,产生的数据就随之释放了,可是在某些情况下,我们希望服务器保存我们的一些数据,方便下次请求(比如网站的账户登录信息,等等)。如果要保存这些发送中的数据,就要用到会话技术(Cookie技术本节不涉及),服务器会将每个浏览器的单独标识,将每个浏览器需要保存的数据,保存下来,当下次需要这些保存的数据,就可以取出来用。 正式点说,会话技术(Session)服务器端保存浏览器请求数据的一项技术,数据是以键值对的形式保存到服务器内存中,可以解决无状态协议带来的弊端,减少每次请求的数据量,提高了性能。2、.Net Core配置
接下来,了解一下,如何在ASP.NetCore中配置使用会话技术2.1 startup
首先需要先配置一下,在startup文件中配置一下Session服务,然后添加Session中间件,需添加在路由中间件之前:
//启用内存缓存(该步骤需在AddSession()调用前使用)
services.AddDistributedMemoryCache();
services.AddSession(options =>
{
options.Cookie.Name = ".AdventureWorks.Session";
options.IdleTimeout = TimeSpan.FromSeconds(2000);//设置session的过期时间
options.Cookie.HttpOnly = true;//设置在浏览器不能通过js获得该cookie的值
});
此处介绍一下为什么要添加分布式内存缓存,官方文档的原文是:启用会话中间件,第一步是:添加任何IDistributedCache内存缓存。IDistributedCache实现用作会话后备存储,这能够保证会话数据的持久和稳定性,以及提高会话的性能,因为会话不像cookie,它的生命周期还是比较长的。
2.2 添加中间件
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSession();//添加会话中间件
app.UseRouting();//路由中间件
app.UseAuthorization();
3、使用
配置好了以后,通过Http上下文对象就可以使用了。要引用如下命名空间,需要其中的Session对象的支持。写入数据的方法有三种:
using Microsoft.AspNetCore.Http;
HttpContext.Session.方法();//使用会话
3.1 在MVC Controller里使用HttpContext.Session
从nuget安装Microsoft.AspNetCore.Mvc引用,直接使用自带的方法进行设置和获取session。不过自带的方法设置和获取的session值是byte[]类型的,可以从nuget安装并引用Microsoft.AspNetCore.Http并使用里面的扩展方法。
public class HomeController : Controller
{
public IActionResult Index()
{
HttpContext.Session.SetString("code", "123456");
return View();
}
public IActionResult About()
{
ViewBag.Code = HttpContext.Session.GetString("code");
return View();
}
}
3.2 如果不是在Controller里,你可以注入IHttpContextAccessor
public class SessionTestClass
{
private readonly IHttpContextAccessor _httpContextAccessor;
private ISession _session => _httpContextAccessor.HttpContext.Session;
public SomeOtherClass(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public void Set()
{
_session.SetString("code", "123456");
}
public void Get()
{
string code = _session.GetString("code");
}
}
3.3 Isession的扩展 存储复杂对象
使用范例:
public static class SessionExtensions
{
public static void SetObjectAsJson(this ISession session, string key, object value)
{
session.SetString(key, JsonConvert.SerializeObject(value));
}
public static T GetObjectFromJson<T>(this ISession session, string key)
{
var value = session.GetString(key);
return value == null ? default(T) : JsonConvert.DeserializeObject<T>(value);
}
}
var myTestObject = new MyTestClass();
HttpContext.Session.SetObjectAsJson("SessionTest", myTestObject);
var myComplexObject = HttpContext.Session.GetObjectFromJson<MyClass>("SessionTest");
4、前后端分离开发跨controller获取不到session
理清思路:前后台分离项目session不能获取到的原因是因为跨域导致请求无法携带和服务器对应的cookie,不是因为前后台分离!前后台分离项目涉及跨域,但是通过一些手段可以避免跨域如nginx反向代理代理到同一个域下,其他方式如CORS过滤跨域都可以让ajax带上cookie,既然能带上cookie那就可以在前后台使用session了吧。
4.1 简单介绍
登录是一个网站最基础的功能。有人说它很简单,其实不然,登录逻辑很简单,但涉及知识点比较多,如:
密码加密、cookie、session、token、JWT等。
我们看一下传统的做法,前后端统一在一个服务中:
如图所示,逻辑处理和页面放在一个服务中,用户输入用户名、密码后,后台服务在session中设置登录状态,和用户的一些基本信息,
然后将响应(Response)返回到浏览器(Browser),并设置Cookie。下次用户在这个浏览器(Browser)中,再次
访问服务时,请求中会带上这个Cookie,服务端根据这个Cookie就能找到对应的session,从session中取得用户的信息,从而
维持了用户的登录状态。这种机制被称作Cookie-Session机制。
近几年,随着前后端分离的流行,我们的项目结构也发生了变化,如下图:
我们访问一个网站时,先去请求静态服务,拿到页面后,再异步去后台请求数据,最后渲染成我们看到的带有数据的网站。在这种结构下,
我们的登录状态怎么维持呢?上面的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发送相应的请求,这里需要进行额外的配置一. 在请求的全局配置中添加如下配置
这段配置的主要功效是将header中的Access-Control-Allow-Credentials设置为true,意为开启cookie的认证请求。这个属性默认是关闭的 完成以上的配置问题大概也就解决了,于是再启动一波
axios.defaults.withCredentials=true;
二. 后端跨域之前的配置
之前的配置是这样的,由于.net core新版本出来之后,AllowAnyOrigin()和AllowCredentials()不能一起使用,于是就懒省事的把AllowCredentials()给注释了(自个给自个埋了个坑。。。)导致请求报跨域的错
services.AddCors(options =>
{
options.AddPolicy("all", builder =>
{
builder.AllowAnyOrigin() //允许任何来源的主机访问
.AllowAnyMethod()
.AllowAnyHeader();
//.AllowCredentials();//指定处理cookie
});
});
三. 后端跨域最终配置
由于AllowAnyOrigin()和AllowCredentials()不能同时使用,我们便使用WithOrigins()来限制指定的主机访问
services.AddCors(options =>
{
options.AddPolicy("all", builder =>
{
builder.WithOrigins("http://localhost:9000") //允许任何来源的主机访问
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();//指定处理cookie
});
});