Koa系列3:持久化用户session状态

1. session背景知识:

session也被称为“会话控制”,顾名思义,它用于控制网络会话,如用户登录信息、购物车中的商品
session中的数据是保存在服务器端的,在服务器端有很多种存储方式,既可以直接保存在内存中,也可以保存在Redis、MongoDB、Mysql等数据库中。但是session中的数据一般都是在短时间内高频访问的,需要保证性能(在内存中缓存,读写快),同时这样容易丢失数据,如遇到服务器重启的情况。因此比较好的方式是内存缓存配合外部数据库对session做一个持久化,方便丢失数据后的数据找回。

2. 将用户session信息持久化:

Koa中有不少中间件提供了session数据的持久化功能,这里我通过两种方法来实现,大家根据自己的需求来取用:

(1). 将session存放在Mysql数据库中

需要用到中间件:
koa-session-minimal 适用于koa2 的session中间件,提供存储介质的读写接口 。
koa-mysql-sessionkoa-session-minimal中间件提供Mysql数据库的session数据读写操作。
示例代码:

const Koa = require('koa')
const session = require('koa-session-minimal')
const MysqlSession = require('koa-mysql-session')

const app = new Koa()

// 配置存储session信息的mysql
let store = new MysqlSession({
  user: 'root',
  password: 'yourpassword',
  database: 'app_database',
  host: '127.0.0.1',
})

// 存放sessionId的cookie配置,根据情况自己设定
let cookie = {
  maxAge: 20*60*1000, // cookie有效时长(ms)
  expires: '',  // cookie失效时间
  path: '', // 写cookie所在的路径
  domain: '', // 写cookie所在的域名
  httpOnly: true, // 是否只用于http请求中获取
  overwrite: true,  // 是否允许重写
  secure: '',
  sameSite: '',
  signed: true,
}

// koa-session-minimal需要一个options对象参数:
/***
 * key:会话cookie名称和商店密钥前缀
   store:会话外部存储
   cookie:cookie选项,可以是对象(静态cookie选项)或返回对象的函数(动态cookie选项)。
   只有maxAge,path,domain,secure,httpOnly是支持的。
 */
app.use(session({
  key: 'SESSION_ID',
  store: store,
  cookie: cookie
}))

//下面就可以在自己的路由中间件中来定义session信息了
//每次请求路由接口,都会对session信息进行更新,请自行验证
app.use( async ( ctx ) => {
  // 设置session
  if ( ctx.url === '/set' ) {
    ctx.session = {
      user_id: Math.random().toString(36).substr(2),
      count: 0
    }
    ctx.body = ctx.session
  } else if ( ctx.url === '/' ) {

    // 读取session信息
    ctx.session.count = ctx.session.count + 1
    ctx.body = ctx.session
  } 

})

app.listen(3000)
console.log('[demo] session is starting at port 3000')

查看数据库,多了一个_mysql_session_store表:

image.png

(2). 将session存放在Redis中:

需要用到的中间件
redis
koa-redis
koa-session-minimal
示例代码:

const session = require('koa-session-minimal');
const redisStore = require('koa-redis');
const Koa = require('koa');
const redis = require('redis');
//实例化redis数据库,并连接,生成redis客户端对象
const client = redis.createClient(6379, "127.0.0.1");

const app = Koa ();
app.keys = ['keys', 'keykeys'];

const options = {client: client, db: 1};
const store = redisStore(options);
app.use(session({
  key: 'SESSION_ID',
  store: store,
  cookie: cookie //与上面的示例代码的参数一样
}));

//以上是为session的外部存储配置了redis的一个客户端,不像前面的koa-mysql-session,
//已经封装了存入session的操作,这里需要在不同的路由中间中通过store.client对象
//的一些原生方法对session数据进行向redis的增删改查
app.use(function *() {
  switch (this.path) {
    case '/get':
      get.call(this);
      break;

    case '/testKV':
      // 保存key value
      if (this.query.adminId) {
        yield store.client.set("test1", this.query.adminId);
      }
      //同步读取key value
      this.body = yield store.client.get("test1");
      break;

    case '/testHM':
      //操作hashmap
      var result = yield store.client.hmset("hosts", "mjr", "123", "another", "23", "home", "1234");
      console.log(result);

      var obj = yield store.client.hgetall("hosts")
      console.dir(obj);
      //获取hashmap key的值
      this.body = yield store.client.hget("hosts", "home");
      break;

    case '/testSet':
      //保存set
      var key = "key1";
      store.client.sadd("key1", "v1");
      store.client.sadd("key1", "v2");
      store.client.sadd("key1", "v3");
      //读取set
      store.client.multi()
        .sismember(key, 'v1')
        .smembers(key)
        .exec(function (err, replies) {
          console.log("MULTI got " + replies.length + " replies");
          replies.forEach(function (reply, index) {
            console.log("Reply " + index + ": " + reply.toString());
          });
        });
      //读取set
      this.body = yield store.client.smembers("key1");
      break;

    case '/testList':
      //保存list
      store.client.rpush("mylist", "bbb")
      store.client.rpush("mylist", "ccc")
      store.client.lpush("mylist", "aaa")
      this.body = yield store.client.rpop("mylist");
      break;

    case '/remove':
      remove.call(this);
      break;

    case '/regenerate':
      yield regenerate.call(this);
      break;
  }
});

function get() {
  var session = this.session;
  session.count = session.count || 0;
  session.count++;
  var test = store.client.get("test");
  console.log(test);
  this.body = session.count;
}

function remove() {
  this.session = null;
  this.body = 0;
}

function *regenerate() {
  get.call(this);
  yield this.regenerateSession();
  get.call(this);
}

app.listen(8080);

以上是将session信息持久化到mysql和redis中的具体代码,发送回浏览器的cookie信息还是会在指定失效时间失效,只是在过期之前一旦发生session数据的意外丢失,可以通过mysql或redis对session的存储来恢复数据。

你可能感兴趣的:(Koa系列3:持久化用户session状态)