会话管理中间件:koa-session
前言
koa-session官网对于浏览器端的session,通常会使用cookie来存储session标识。在用户首次访问服务端时,服务端会生成一个唯一的session标识,并将其存储在cookie中返回给浏览器。
随后,每次浏览器发送请求到服务端时,会自动携带上一次存储在cookie中的session标识。服务端通过解析请求中的session标识,可以识别出当前请求属于哪个session。
服务端会根据session标识来查找相应的session数据,并进行相应的处理。在处理完请求之后,服务端会更新session数据,并将最新的session标识返回给浏览器,以便下次请求使用。
这种基于cookie的session机制可以确保同一个浏览器在一次会话中持续保持与服务端的连接,并共享会话数据。通过每次请求携带最新的session标识,可以保证同一个session会话的持续性和一致性。此外,为了防止篡改或伪造session标识,可以结合使用签名(sig)等方式,增加session的安全性。Koa
简单的会话管理中间件,基于cookie机制,并支持外部存储(比如redis、db数据库等存储方式)
默认配置是不带数据库的,也就是基于内存的存储,这虽然访问快,但是一旦服务重启,所存储的信息就无效了,因此一般需要结合对应的数据库存储库来保证数据的持久有效性!
👇 是session在浏览器中的一个工作过程以及对应的执行结果:
如何使用
1 | const session = require('koa-session'); |
上述中的CONFIG
配置除了key之外的其他属性,都将通过ctx.cookies.get()
或者ctx.cookies.set()
方法来进行设置到网络请求的配置中
参数说明
koa-session
中配置的参数不只上述提及到的, 👇 将具体分析各个参数已经其使用方式:
上述中的store
与ContextStore
两者有一个微妙的不同,虽然都是必须要拥有get
、set
、destroy
方法,但两者是互斥的,而且ContextStore
的优先级要大于store
,具体关于两者的使用以及运行过程,将在下面的源码分析中说明!
源码分析
koa-session
默认采用内部存储,可以理解为存储在内存中,然后针对每次客户端的请求,都自动去内存中匹配到对应的上次请求用户的标识,用以区分用户,这个在最开始的前言我们也已经具体讲解过了,现在我们要从入口开始,具体分析这个koa-session
的执行过程,一切从调用的入口开始:
1 | module.exports = function(opts, app){ |
👽 这里通过扩展请求上下文,也就说明了为什么我们使用了koa-session
中间件之后,可以通过ctx.session
来访问到上下文会话了,因为每一次的请求,都会创建一新的session
来作为请求上下文的访问,通过session
可以进行相关的读写操作,这里的session
其实就是SessionContext
中的session
上下文中新增的参数
经过
koa-session
中间件之后,新增的属性有:session
、sessionOptions
、Symbol类型的ContextSession
1 | // 自定义的扩展请求体上下文的方法 |
😕 而这个CONTEXT_SESSION
被访问的时候,它是采用一种懒加载的方式来执行的,如下所示:
1 | [CONTEXT_SESSION]: { |
:point-right: 因此,当通过const sess = ctx[CONTEXT_SESSION]
的时候,将自动创建一ContextSession
对象,这个对象就是主要负责管理session
会话的所有功能
1 | class ContextSession{ |
🌠 当响应一个请求的时候,该中间件将创建对应的ContextSession
上下文会话,并将其store
属性与ContextStore
或者外部存储store
关联,也就是说,如果我们传递了ContextStore
或者store
的话,那么后续关于session的key的获取与赋值,将从传递进来的外围store来处理, 😕 这里有一个区别,就是ContextStore
都是以ctx
作为其中的参数,因此我们可以在使用外围store处理session的key时候,结合ctx
中的信息来进行一个组合判断校验!
👽 当我们通过在中间件中使用this.session.views
的时候,将会自动执行ContextSession.get()
方法(这里假设我们没有使用外部存储,默认用cookie),则将自动访问ContextSession.initFromCookie()
方法,从cookie中来创建一个会话!
1 | initFromCookie(){ |
🌠 这里我们平时所使用的this.session
其实就是koa-session
库中的Session
对象,我们可以直接使用其相关的api:
- toJSON(): 获取session会话中的key的所组成的对象,我们上述所传递的自定义属性
views
就直接存储于此; - length: 获取session会话存储的对象的key的数量;
- maxAge: 获取session会话超时时长;
- externalKey: 获取配置的外部键;
- save(): 将当前请求中的会话给存储下来;
- regenerate(): 重新生成一个新的会话;
- manuallyCommit(): 手动commit一个session;
- commit(): 触发当前session所在的上下文会话中的commit方法,实现会话的存储;
真正commit的过程
当
commit
动作发生的时候,最终由ContextSession
中的commit({})
来完成最终的提交保存动作
1 | async commit({ save = false, regenerate = false } = {}){ |
🌠 这里的commit
其实是提交前的准备工作,根据_shouldSaveSession()
的返回值来判断是否需要执行最终的save()
操作,_shouldSaveSession()
主要用于结合过期参数(expire
、maxAge
)来决定是否要执行保存动作,然后最终触发save()
进行session会话的存储,一般默认是存储于cookies
中,因此在最开始的前言中我们可以看到在浏览器客户端中的请求响应tou中的cookies可以看到所存储的值!
1 | async save(changed){ |
🌠 这里我们可以发现默认的session都存储于cookies中!!
学到什么
- 当我们需要在对象中定义某些属性,然后不想外部直接放回到这些属性,可以采用在对象内部定义Symbol参数,然后以Symbol作为属性,然后对其进行相应的赋值操作;
- 当我们想要懒加载对象的时候,可以等到需要访问到对象的时候才进行该对象的初始化工作,这种我称之为显示的懒加载,还存在另外一种懒加载的方式:通过
Object.defineProperty()
的方式,通过重载其get()
方法,使得我们在访问的时候,直接无视初始化的工作; ContextStore
可以结合请求上下文ctx,实现上下文与cookies结合,并开放额外的方法api,可配合数据库,进行session的存取毁工作!