一、前沿
我们上一篇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比较正常的一个验证登陆流程