前端登陆之session篇(redis缓存)

  • Post author:
  • Post category:其他


一、前沿

我们上一篇cookie篇中可以看到,cookie存储在客户端,也就是浏览器或者其他端的客户端上面,他的优点很明显,就是不占用服务端的资源,但是缺点也很明显,那就是存储的量有限制,只能是字符串的形式,数据容易被获取,也容易被篡改,虽然可以加密,但是也容易丢失问题也不好解决,一个清楚浏览器缓存可能就会造成数据丢失,而session相当于和cookie是相反的,优点变缺点,缺点变优点,他的存储是任何格式的,数据不容易丢失,等等,但是缺点也是很明显,就是占用服务资源,大型的项目,会有服务器集群来专门存储这个session.

二、session中间件简介

express-session中间件将会话数据存储在服务器上,它仅将会话标识(而非会话数据)保存在 cookie 中, express-session不再依赖cookie-parser,直接通过req/res读取/写入,它默认存储位置内存存储(服务器端),它有一些方法来使用session:@method session(options)/Session.destroy()/Session.reload()/Session.regenerate()/Session.save()

也可以通过option来设置session存储,除了session ID外,session中的任何数据都不存储在cookie中。而这个sessionID也是通过加密存储的,它的key默认为connect_id,可以使用name进行修改;

 * 例:
{
   name: 'zjh',
   secret: 'keyboard cat', 
   重新保存:强制会话保存即使是未修改的。默认为true但是得写上
   cookie: ('name', 'value', { path: '/', httpOnly: true,secure: false, maxAge:  60000 }), 
   resave: true, 
   强制“未初始化”的会话保存到存储。 
   saveUninitialized: true, 
}

三、使用session实现登陆验证

下面介绍session中间件的简单使用,我们只需要在服务中引入中间件,然后使用,配置一些参数:

const session = require("express-session")

app.use(session({
  secret:"", //密钥默认为conect_id
  name:"zjh",
}))

然后讲之前的登陆接口修改,只需要在验证成功后在req.session中添加值,就可以在客户端请求的时候,给cookie中添加session;

router.post("/login",asyncHandler(
    async (req,res,next) =>{
    const result = await user.login(req.body.loginId,req.body.loginPwd)
    if (result) {
        const value = result.id
        cryptor.encrypt(value.toString())

        req.session.loginUser = result

      }
      return result;
    }
))

而在调用其他接口的时候,就会验证session,我们就可以直接获取req.session的loginUser去验证是否通过。

module.exports = (req, res, next) => {
  
  if(req.session.loginUser){
    console.log("认证通过啦~~~");
    next()
  }else{
    handleNonToken(req,res,next);
  }
};

以上就是session中间件的使用方法,但是这种方法,是将session存储在服务端的内存中,这样的话就存在一些问题难以处理,比如当流量变得非常大以后,内存不够使用,再有一个就是没有持久缓存,当我们重新启动服务器的时候,这个session的数据就会消失,所以通常在服务端,会讲session存储在数据库中,这里引入redis;

四、redis简介

redis其实就是一种nosql数据库,用来存储数据,nosql并没有表结构这类东西,数据都是以键值对的方式存储,redis最大特点就是速度快,因为数据都是保存在内存中,我们可以将用户频繁查询的数据存入redis中,以提高查询性能

 * @method
 * 命令行启动/关闭redis
 * 后台启动:
 * brew services start redis@6.2
 * brew services stop redis@6.2
 * 前台启动:
 * /opt/homebrew/opt/redis@6.2/bin/redis-server /opt/homebrew/etc/redis.conf
 */

这里我们使用前端启动,后端启动可以在redis里面用命令操作,命令操作这里就不说啦,还有redis的下载启动都可以百度,

启动后大概是这样。

/**
 * 说明:
 * 将nodejs回调风格的函数包装成Promise
 * @method
 * @promisifyAll
 * bluebird.promisifyAll将nodejs回调风格的函数包装成Promise对象
 */
const redis = require("redis")
const  bluebird  = require("bluebird") 

bluebird.promisifyAll(redis)
const Client = redis.createClient()

Client.on("error",(err)=>{
    console.log("redis---err",err);
})

module.exports =  Client

在js中我们可以这样使用,用异步包裹,让redis的操作异步化,这样方便使用;

client.set("name","zjh",(err,reply) =>{
    console.log(reply);
})

client.get("name",(err,reply) =>{
    console.log(reply);
})

client.hset("name","zjh",(err,reply) =>{
    console.log(reply);
})

client.hget("name",(err,reply) =>{
    console.log(reply);
})

client.select("name",(err,reply) =>{
    console.log(reply);
})

这里的方法,可以看出,和在终端操作大概相同;

五、引入connectRedis和uuidV4

connectRedis redis会话存储简介, connect-redis是一个由node_redis支持的Redis会话存储,并且非常快,Redis客户端是必须的。使用 client 参数直接传递现有客户端,也可以使用host、 port、或 socket参数为你创建

 * @param {Object} options
 * 可选参数:
 * @type {ttl} 过期时间,默认是session.maxAge, 或者是一天 
 * @type {disableTTL} 是否允许redis的key有过期时间。这个值优先于ttl 
 * @type {db} redis哪个数据库,默认是0 
 * @type {pass} 密码
 * @type {prefix} key的前缀,默认是 'sess:'
 * @type {unref} 这个方法作用于底层socket连接,可以在程序没有其他任务后自动退出 
 * @type {serializer} 包含stringify和parse的方法,用于格式化存入redis的值。默认是JSON 
 * @type {logErrors} 是否打印redis出错信息,默认false 
   如果值为true,则会提供一个默认的处理方法(console.error)
   如果是一个函数,则redis的报错信息由它来处理
   如果值为false,则不处理出错信息
   此列表中未包含的任何选项将直接传递给redis createClient()方法 
 * @method自定义Redis客户端
 * 例:
   var session = require('express-session');
   var RedisStore = require('connect-redis')(session);
   app.use(session({
    secret: config.session_secret,
    store: new RedisStore({
    port: config.redis_port,
    host: config.redis_host,
    db: config.redis_db,
    pass: config.redis_password,
    }),
    resave: false,
    saveUninitialized: false,
    }));   

@uuidV4 命名id简介,需要给某些数据定义一个唯一标识符,便于寻找,关联,给某些组件定义唯一key,或者属性的唯一性

 * 例:
    const uuidv4 = require('uuid/v4');uuidv4(); 
   '10ba038e-48da-487b-96e8-8d3b99b6d18a' 
const session = require("express-session")
const connectRedis = require("connect-redis")
const client = require("../redis/redis")
const uuidV4 = require("uuid")
// 会话存储
const redisStore = connectRedis(session)
const store = new redisStore({
    //存储redis数据库
    client
})

const sess = {
    cookie: {
        maxAge: 60*1000 //有效期,60秒刷新一次cookie
    }
}
app.use(session({
    ...sess,
    name:"zjh", //name为cookie名称,默认为connect_sid
    genid:(req) =>{
        console.log(req,"----------------req");
        return uuidV4() //使用uuids作为session的id
    },
    store,
    secret: "",
    resave: true, //存储到磁盘
    saveUninitialized: false, //初始回话不存储
}))

现在的session就是存储在redis数据库中了,而不是存在node服务内存中,且使用uuid之后sessionID也是加密过的唯一id。这样就实现了session比较正常的一个验证登陆流程



版权声明:本文为weixin_72913454原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。