当前位置: 首页 > 后端技术 > Node.js

Express使用mongodb来管理会话存储,connect-mongo模块介绍

时间:2023-04-03 13:01:11 Node.js

介绍在我之前的文章中,express-session这本书提到express-session可以替代session存储。那么在这篇文章中,我们就来谈谈express在会话管理的时候,是如何将session数据保存到外部数据库中的。在本文中,我们使用mongodb作为会话存储数据库。本文使用的模块及版本号列表:模块名称版本号express4.16.4mongodb3.1.8express-session1.15.6connect-mongo2.0.3connect-mongo特性支持Express5支持所有版本Connect支持Mongoose>=4.1.2+支持nativeMongodbdriver>=2.0.36支持Node.js46810支持Mongodb>=3.0前期分析由于mongodbclient和server可以是多对多的关系,所以有组合如下。一个客户端连接到多个服务器。多个客户端连接到一台服务器。多个客户端连接到多个服务器。一个客户端连接到一台服务器。本文主要讲解一个客户端连接一台服务器。端口,希望共享同一个mongodb驱动实例。但是一般情况下,我们的mongodb数据库不能只用于session管理任务,所以本文复用了同一个连接(端口)。只需复用相同的连接就可以做到,因此很容易使用单独的驱动程序实例进行会话管理。首先,我们引入所有模块:constExpress=require('express')(),MongoClient=require('mongodb')。MongoClient,ExpressSession=require('express-session'),MongoStore=require('connect-mongo')(ExpressSession);connect-mongo好像需要包裹express-session,这一步搞定了。接下来我们定义几个用于连接数据库的常量:constUrlOfDb='mongodb://localhost:27017',NameOfDb='demo',Client=newMongoClient(UrlOfDb);//创建mongodbClient客户端连接数据库:Client.connect((error)=>{if(error){throwerror;}});使用一张数据表,查询几条数据:constDataBase=Client.db(NameOfDb),Collection=DataBase.collection('sessions');Collection.find({}).toArray((error,result)=>{if(error){throwerror;}for(constelementofresult){console.log(element);}});至此我们还没有做session管理,可以替换本例中的数据表名来测试运行是否正常。完整代码:constExpress=require('express')(),MongoClient=require('mongodb').MongoClient,//获取数据库驱动ExpressSession=require('express-session'),//获取会话中间件MongoStore=require('connect-mongo')(ExpressSession);//获取Session存储插件constUrlOfDb='mongodb://localhost:27017',NameOfDb='demo',Client=newMongoClient(UrlOfDb);//创建一个客户端Client.connect((error)=>{if(error){throwerror;}constDataBase=Client.db(NameOfDb),//获取数据库Collection=DataBase.collection('sessions');//获取数据表//查询数据表Collection.find({}).toArray((error,result)=>{if(error){throwerror;}for(constelementofresult){console.log(element);}});});现在让我们使用express-session中间件并替换默认存储://+++++constDataBase=Client.db(NameOfDb),//获取数据库Collection=DataBase.collection('sessions'),//获取数据表MongoStoreInstance=newMongoStore({//创建一个存储实例,为数据库对象传入db参数db:DataBase});//使用中间件Express.use(ExpressSession({secret:'hellomongo',//cookiesignaturecookie:{maxAge:1800000},rolling:true,saveUninitialized:true,resave:false,store:MongoStoreInstance//替换默认存储}));//+++++++注:connect-mongo会在数据库下创建一个session数据表(如果没有这个数据表的话)。添加一个路由来完成一个简单的验证,用来测试是否正常工作:Express.get('/',(request,response)=>{if(request.session.name){response.send(`欢迎回来${request.session.name}`);返回;}//使用查询字符串作为保存的信息request.session.name=request.query.name;request.session.pwd=request.query.pwd;response.send(`欢迎登录${request.session.name}`);});//启动服务器Express.listen(8888,function(){console.log('serverislistening8888port!');});完整代码:constExpress=require('express')(),MongoClient=require('mongodb').MongoClient,ExpressSession=require('express-session'),MongoStore=require('connect-mongo')(ExpressSession);constUrlOfDb='mongodb://localhost:27017',NameOfDb='demo',Client=newMongoClient(UrlOfDb);functiondestroyDb(Client){returndestroyDb=function(){constinfo='客户端已关闭!';客户端.close();客户端=空;控制台日志(信息);返回信息;}}Client.connect((error)=>{if(error){throwerror;}constDataBase=Client.db(NameOfDb),Collection=DataBase.collection('sess离子'),MongoStoreInstance=newMongoStore({db:DataBase});Express.use(ExpressSession({secret:'hellomongo',cookie:{maxAge:1800000},rolling:true,saveUninitialized:true,resave:false,store:MongoStoreInstance}));//使用闭包将关闭的数据库挂载到全局destroyDb(Client);//演示连接的重用Collection.find({}).toArray((error,result)=>{if(error){throwerror;}for(constelementofresult){console.log(element);}});Express.get('/',(request,response)=>{if(request.session.name){response.send(`欢迎回来${request.session.name}`);return;}request.session.name=request.query.name;request.session.pwd=request.query.pwd;response.send(`欢迎登录${request.session.name}`);});Express.get('/closedatabase',(request,respnose)=>{respnose.send(destroyDB());});Express.listen(8888,function(){console.log('服务器正在监听8888端口!');});});注意:我没有删除数据库表的常规输出,在这个例子启动的时候,你会发现它们共享同一个连接,启动时会先输出数据表中的内容。测试,在浏览器中输入以下内容:http://localhost:8888/?name=ascll&pwd=123456浏览器输出:欢迎登录ascll,再次直接访问该页面:http://localhost:8888/浏览器输出:welcomeback此时在数据库中手动查询ascll,或者重启项目,你会发现第二次在控制台留下的Session记录如上:{_id:'qbP36wE0nJkvtyNqx_6Amoesjjcsr-sD',expires:2018-12-14T08:27:19.809Z,session:'{"cookie":{"originalMaxAge":1800000,"expires":"2018-12-14T08:20:21.519Z","httpOnly":true,"path":"/"},"name":"ascll","pwd":"123456"}'}使用summary介绍connect-mongo和express-session然后调用connect-mongo调用express-sessino获取返回的类上一步,然后使用express-session中间件为store选择这个类的实例对象api来创建Express4.x、5.0和Connect3.x:constsession=require('express-session');constMongoStore=require('connect-mongo')(session);app.use(session({secret:'foo',store:newMongoStore(options)}));Express2.x,3.xandConnect1.x,2.x:constMongoStore=require('connect-mongo')(快递);app.use(express.session({secret:'foo',store:newMongoStore(options)}));使用mongoose连接到MongoDbconstmongoose=require('mongoose');//基本上使用mongoose.connect(connectionOptions);app.use(session({store:newMongoStore({mongooseConnection:mongoose.connection})}));//重用连接的推荐方式constconnection=mongoose.createConnection(connectionOptions);app.use(session({store:newMongoStore({mongooseConnection:connection})}));在这种情况下,您需要使用一个mongodb驱动的数据库实例将其传递给connect-mongo。如果数据库没有打开,connect-mongo会自动为你连接。/*获取数据库实例的方式有很多种。详情请参考官网文档。*/app.use(session({store:newMongoStore({db:dbInstance})//不要忘记MongoStore是connect-mongo传递给express-session的返回函数}));//或者你可以使用Promise版本app.use(session({store:newMongoStore({dbPromise:dbInstancePromise})}));通过连接字符串创建连接//基本用法app.use(session({store:newMongoStore({url:'mongodb://localhost/test-app'})}));//高级用法app.use(session({store:newMongoStore({url:'mongodb://user12345:foobar@localhost/test-app?authSource=admins&w=1',mongoOptions:advancedOptions//详情见下文})}));事件MongoStore实例有以下事件:事件名称描述回调参数createsession触发sessionIdtouchsession已获取但未修改sessionIdupdatesession已更新sessionIdsetsession已创建或更新(为了兼容性)sessionIddestroysession在我们之前的示例中使用sessionId后被销毁添加以下代码://+++MongoStoreInstance.on('create',(sessionId)=>{console.log('create',sessionId);});MongoStoreInstance.on('touch',(sessionId)=>{console.log('create',sessionId);});MongoStoreInstance.on('update',(sessionId)=>{console.log('update',sessionId);});MongoStoreInstance.on('set',(sessionId)=>{console.log('set',sessionId);});MongoStoreInstance.on('destroy',(sessionId)=>{console.log('destroy',sessionId);});//+++清除cookie后,再次运行服务器。再进行一些操作后,就可以看到session的创建和修改了。session过期处理的基本处理方法connect-mongo只会使用配置过期时间的cookie,如果没有设置会创建新的cookie并使用tll选项指定过期时间:app.use(session({store:newMongoStore({url:'mongodb://localhost/test-app',ttl:14*24*60*60//默认过期时间是14天})}));注:过期时间会在用户每次访问时刷新。删除过期的会话。默认情况下,connect-mongo使用MongoDB的TTL收集功能(2.2+)来自动删除过期的会话。但是你可以修改它的这种行为。connect-mongo会在最开始创建一个TTl索引,前提是你的Mongodb版本是(2.2+)并且有执行这个操作的权限。app.use(session({store:newMongoStore({url:'mongodb://localhost/test-app',autoRemove:'native'//默认})}));注意:这种默认行为不适合高并发,此时需要禁用默认模式,然后自己定义TTl索引。使用兼容模式。如果你使用老版本的Mongodb或者不想创建TTL索引,你可以指定一个时间间隔让connect-mongo删除这些过期的session.app.use(session({store:newMongoStore({url:'mongodb://localhost/test-app',autoRemove:'interval',autoRemoveInterval:10//单位分钟})}));禁用过期会话删除app.use(session({store:newMongoStore({url:'mongodb://localhost/test-app',autoRemove:'disabled'})}));sessionlazyupdate如果你用的express-session版本>=1.10,那么不希望每次用户浏览页面或者刷新页面的时候,都要重新保存一次。你可以限制updatesession.app.use(express.session({secret:'keyboardcat',saveUninitialized:false,//不保存不创建sessionresave:false,//不修改不保存store:newMongoStore({url:'mongodb://localhost/test-app',touchAfter:24*3600//以秒为单位指定触发间隔})}));通过这样设置session,不管用户浏览页面多少次,刷新多少次,一个小时内只会在24次触发一次。除了修改会话。其他选项集合指定缓存数据表的名称。默认会话fallbackMemory回退处理默认使用MemoryStore进行存储。反序列化使用原生JSON.xxx处理。serialize自定义序列化函数unserialize自定义反序列化函数transformId将sessionId转换成你想要的任意key然后存储有两点:connect-mongo在Mongodb客户端正常关闭后会报错。虽然会被Express拦截,但是这个模块并没有提供错误事件。必须同步挂载Express中间件?在我的示例中,尝试异步加载express-session中间件,但失败的中间件没有任何效果。connect-mongo模块npm地址https://www.npmjs.com/package...