node.js中express-session配置项详解
官方地址:
作用:用指定的参数创建一个session中间件,sesison数据不是保存在cookie中,仅仅sessionid保存到cookie中,session的数据仅仅保存在服务器端
警告:默认的服务器端的session存储,memorystore不是为了生产环境创建的,大多数情况下会内存泄露,主要用于测试和开发环境
接受的参数:
cookie:也就是session id的cookie,默认是{ path: '/', httponly: true, secure: false, maxage: null }.
var cookie = module.exports = function cookie(options) { this.path = '/'; this.maxage = null; this.httponly = true; if (options) merge(this, options); this.originalmaxage = undefined == this.originalmaxage ? this.maxage : this.originalmaxage; //默认的originalmaxage就是this.maxage也就是null,如果指定了originalmaxage那么就是用户指定的值 };
genid:产生一个新的sessionid的函数,一个返回值是string类型的函数会被作为sessionid.这个函数第一个参数是req,所以如果你想要req中的参数产生sessionid还是很不错的
默认函数是使用uid-safe这个库产生id值(产生一个算法上安全的uid,可以用于cookie也可以用于url。和rand-token和uid2相比,后者由于使用了%导致uid产生偏态,同时可能对uid产生不必要的截断。我们的uid-safe使用的是base64算法,其函数uid(bytelength, callback)中第一个参数是比特长度而不是字符串长度)
app.use(session({ genid: function(req) { return genuuid() // use uuids for session ids }, secret: 'keyboard cat' })
源码片段:
function generatesessionid(sess) { return uid(24); } var generateid = options.genid || generatesessionid; //如果用户没有传入genid参数那么就是默认使用generatesessionid函数来完成
name:在response中sessionid这个cookie的名称。也可以通过这个name读取,默认是connect.sid。如果一台机器上有多个app运行在同样的hostname+port, 那么你需要对这个sessin的cookie进行切割,所以最好的方法还是通过name设置不同的值
name = options.name || options.key || 'connect.sid' //很显然cookie的name默认是connect.sid,而且首先获取到的name而不是key r cookieid = req.sessionid = getcookie(req, name, secrets);
resave:强制session保存到session store中。即使在请求中这个session没有被修改。但是这个并不一定是必须的,如果客户端有两个并行的请求到你的客户端,一个请求对session的修改可能被另外一个请求覆盖掉,即使第二个请求并没有修改sesion。默认是true,但是默认值已经过时,因此以后default可能会被修改。因此好好研究你的需求选择一个最适用的。大多数情况下你可能需要false 最好的知道你的store是否需要设置resave的方法是通过查看你的store是否实现了touch方法(删除那些空闲的session。同时这个方法也会通知session store指定的session是活动态的),如果实现了那么你可以用resave:false,如果没有实现touch方法,同时你的store对保存的session设置了一个过期的时间,那么建议你用resave:true
var resavesession = options.resave; if (resavesession === undefined) { deprecate('undefined resave option; provide resave option'); resavesession = true;//如果用户没有指定resavedsession那么默认就是true }
我们再来看看其他的逻辑
store.get(req.sessionid, function(err, sess){ // error handling //如果报错那么也会创建一个session if (err) { debug('error %j', err); if (err.code !== 'enoent') { next(err); return; } generate(); // no session那么就会创建一个session } else if (!sess) { debug('no session found'); generate(); // populate req.session //如果找到了这个session处理的代码逻辑 } else { debug('session found'); store.createsession(req, sess); originalid = req.sessionid; originalhash = hash(sess); //originalhash保存的是找到的这个session的hash结果,如果明确指定了resave为false那么savedhash就是原来的session的结果 if (!resavesession) { savedhash = originalhash } wrapmethods(req.session); } next(); }); }; };
其中经过了前面的if语句后我们的savedhash就是originalhash,我们看看这个逻辑在判断这个session是否已经保存的时候再次用到了
function issaved(sess) { return originalid === sess.id && savedhash === hash(sess); }
rolling:强制在每一个response中都发送session标识符的cookie。如果把expiration设置为一个过去的时间那么 那么过期时间设置为默认的值。roling默认是false。如果把这个值设置为true但是saveunitialized设置为false,那么cookie不会被包含在响应中(没有初始化的session)
rollingsessions = options.rolling || false;//默认为false
我们看看rolling用于了什么环境了:
//这个方法用户判断是否需要在请求头中设置cookie // determine if cookie should be set on response function shouldsetcookie(req) { // cannot set cookie without a session id //如果没有sessionid直接返回,这时候不用设置cookie if (typeof req.sessionid !== 'string') { return false; } //var cookieid = req.sessionid = getcookie(req, name, secrets); return cookieid != req.sessionid ? saveuninitializedsession || ismodified(req.session) //rollingsessions = options.rolling || false,其中rolling表示sessioncookie在每一个响应中都应该被发送。也就是说如果用户设置了rolling即使sessionid没有被修改 //也依然会把session的cookie发送到浏览器 : rollingsessions || req.session.cookie.expires != null && ismodified(req.session); }
很显然,如果客户端发送的sessionid和服务器的sessionid一致,如果你指定了rolling为true,那么还是会发送这个session的cookie到客户端,但是如果你设置了rolling为false,那么这时候如果同时设置了req.session.cookie.expires,而且这个req.session被修改了这时候还是会把session的cookie发送到客户端!
saveuninitialized:强制没有“初始化”的session保存到storage中,没有初始化的session指的是:刚被创建没有被修改,如果是要实现登陆的session那么最好设置为false(reducing server storage usage, or complying with laws that require permission before setting a cookie) 而且设置为false还有一个好处,当客户端没有session的情况下并行发送多个请求时。默认是true,但是不建议使用默认值。
var saveuninitializedsession = options.saveuninitialized; /如果用户不指定saveuninitializedsession那么提示用户并设置saveuninitializedsession为true if (saveuninitializedsession === undefined) { deprecate('undefined saveuninitialized option; provide saveuninitialized option'); saveuninitializedsession = true; }
我们来看看这个参数用于做什么判断,首先看看shouldsave方法
// determine if session should be saved to store //判断是否需要把session保存到到store中 function shouldsave(req) { // cannot set cookie without a session id if (typeof req.sessionid !== 'string') { debug('session ignored because of bogus req.sessionid %o', req.sessionid); return false; } // var saveuninitializedsession = options.saveuninitialized; // var cookieid = req.sessionid = getcookie(req, name, secrets); return !saveuninitializedsession && cookieid !== req.sessionid ? ismodified(req.session) : !issaved(req.session) }
如果用户指明了不能保存未初始化的session,同时服务器的req.sessionid和浏览器发送过来的不一致,这时候只有在服务器的session修改的时候会保存。如果前面的前提不满足那么就需要看是否已经保存过了,如果没有保存过那么才会保存!
这个参数还被用于决定是否需要把session的cookie发送到客户端:
//这个方法用户判断是否需要在请求头中设置cookie // determine if cookie should be set on response function shouldsetcookie(req) { // cannot set cookie without a session id //如果没有sessionid直接返回,这时候不用设置cookie if (typeof req.sessionid !== 'string') { return false; } //var cookieid = req.sessionid = getcookie(req, name, secrets); return cookieid != req.sessionid ? saveuninitializedsession || ismodified(req.session) //rollingsessions = options.rolling || false,其中rolling表示sessioncookie在每一个响应中都应该被发送。也就是说如果用户设置了rolling即使sessionid没有被修改 //也依然会把session的cookie发送到浏览器 : rollingsessions || req.session.cookie.expires != null && ismodified(req.session); }
如果客户端和服务器端的sessionid不一致的前提下,如果用户指定了保存未初始化的session那么就需要发送,否则就只有在修改的时候才发送
secret:用于对sessionid的cookie进行签名,可以是一个string(一个secret)或者数组(多个secret)。如果指定了一个数组那么只会用 第一个元素对sessionid的cookie进行签名,其他的用于验证请求中的签名。
var secret = options.secret; //unsetdestroy表示用户是否指定了unset参数是destroy,是布尔值 if (array.isarray(secret) && secret.length === 0) { throw new typeerror('secret option array must contain one or more strings'); } //保证secret保存的是一个数组,即使用户传入的仅仅是一个string if (secret && !array.isarray(secret)) { secret = [secret]; } //必须提供secret参数 if (!secret) { deprecate('req.secret; provide secret option'); }
我们看看这个secret参数用于什么情景:
//作用:用于从请求对象request中获取session id值,其中name就是我们在options中指定的,首先从req.headers.cookie获取,接着从req.signedcookies中获取,最后从req.cookies获取 function getcookie(req, name, secrets) { var header = req.headers.cookie; var raw; var val; // read from cookie header if (header) { var cookies = cookie.parse(header); raw = cookies[name]; if (raw) { if (raw.substr(0, 2) === 's:') { //切割掉前面的字符"s:"! val = unsigncookie(raw.slice(2), secrets); //val表示false意味着客户端传递过来的cookie被篡改了! if (val === false) { debug('cookie signature invalid'); val = undefined; } } else { debug('cookie unsigned') } } } // back-compat read from cookieparser() signedcookies data if (!val && req.signedcookies) { val = req.signedcookies[name]; if (val) { deprecate('cookie should be available in req.headers.cookie'); } } // back-compat read from cookieparser() cookies data if (!val && req.cookies) { raw = req.cookies[name]; if (raw) { if (raw.substr(0, 2) === 's:') { val = unsigncookie(raw.slice(2), secrets); if (val) { deprecate('cookie should be available in req.headers.cookie'); } if (val === false) { debug('cookie signature invalid'); val = undefined; } } else { debug('cookie unsigned') } } } return val; }
getcookie方法用于从请求中获取sessionid进行解密,作为秘钥。
// setcookie(res, name, req.sessionid, secrets[0], cookie.data); //方法作用:为http响应设置cookie,设置的cookie是把req.sessionid进行加密过后的cookie,其中name用于保存到客户端的sessionid的cookie的名称 function setcookie(res, name, val, secret, options) { var signed = 's:' + signature.sign(val, secret); //对要发送的cookie进行加密,密钥为secret var data = cookie.serialize(name, signed, options); //其中options中可能有decode函数,返回序列化的cookie debug('set-cookie %s', data); var prev = res.getheader('set-cookie') || []; //获取set-cookie头,默认是一个空数组 var header = array.isarray(prev) ? prev.concat(data) : array.isarray(data) ? [prev].concat(data) : [prev, data]; //通过set-cookie,发送到客户端 res.setheader('set-cookie', header) }
用于setcookie方法,该方法用于对sessionid用指定的秘钥进行签名。
store:保存session的地方,默认是一个memorystore实例
store = options.store || new memorystore // notify user that this store is not // meant for a production environment //如果在生产环境下,同时store也就是用户传入的store(默认为memorystore)是memorystore那么给出警告 if ('production' == env && store instanceof memorystore) { console.warn(warning); } // generates the new session //为用于指定的store添加一个方法generate,同时为这个方法传入req对象,在这个generate方法中为req指定了sessionid,session,session.cookie //如果用户传入的secure为auto, store.generate = function(req){ req.sessionid = generateid(req); req.session = new session(req); req.session.cookie = new cookie(cookieoptions); //用户指定的secure参数如果是auto,那么修改req.session.cookie的secure参数,并通过issecure来判断 if (cookieoptions.secure === 'auto') { req.session.cookie.secure = issecure(req, trustproxy); } }; //查看store是否实现了touch方法 var storeimplementstouch = typeof store.touch === 'function'; //为store注册disconnect事件,在该事件中吧storeready设置为false store.on('disconnect', function(){ storeready = false; }); //为stroe注册connect事件,把storeready设置为true store.on('connect', function(){ storeready = true; }); // expose store req.sessionstore = store;
我们知道这个store是用于保存session的地方,默认是一个memorystore,但是在生产环境下不建议使用memorystore,同时store有很多自定义的方法,如这里就为他添加了generate,connect,disconnect,当然也包含destroy方法。如果你对store感兴趣,可以看看下面这个通用的store具有的所有的方法:
'use strict'; var eventemitter = require('events').eventemitter , session = require('./session') , cookie = require('./cookie') var store = module.exports = function store(options){}; //这个store实例是一个eventemitter实例,也就是说store实例最后还是一个eventemitter实例对象 store.prototype.__proto__ = eventemitter.prototype; //每一个store有一个默认的regenerate方法用于产生session store.prototype.regenerate = function(req, fn){ var self = this; //regenerate底层调用的是destroy方法,第一个参数是req.sessionid,至于回调中的self.generate必须是对容器进行指定的 this.destroy(req.sessionid, function(err){ self.generate(req); fn(err);//最后回调fn }); //调用这个store的destory方法,销毁req.sessionid,销毁成功后通过刚才的store的generate方法产生一个sessionid }; //通过指定的sid加载一个session实例,然后触发函数fn(err,sess) store.prototype.load = function(sid, fn){ var self = this; //最后调用的是store的get方法 this.get(sid, function(err, sess){ if (err) return fn(err); if (!sess) return fn(); //如果sess为空那么调用fn()方法 var req = { sessionid: sid, sessionstore: self }; //调用createsession来完成的 sess = self.createsession(req, sess); fn(null, sess); }); }; //从一个json格式的sess中创建一个session实例,如sess={cookie:{expires:xx,originalmaxage:xxx}} store.prototype.createsession = function(req, sess){ var expires = sess.cookie.expires , orig = sess.cookie.originalmaxage; //创建session时候获取其中的cookie域下面的expires,originalmaxage参数 sess.cookie = new cookie(sess.cookie); //更新session.cookie为一个cookie实例而不再是一个{}对象了 if ('string' == typeof expires) sess.cookie.expires = new date(expires); sess.cookie.originalmaxage = orig; //为新构建的cookie添加originalmaxage属性 req.session = new session(req, sess); //创建一个session实例,其中传入的第一个参数是req,第二个参数是sess也就是我们刚才创建的那个cookie实例,签名为sess={cookie:cookie对象} return req.session; };
unset:对没有设置的req.session进行控制,通过delete或者设置为null。默认是keep,destory表示当回应结束后会销毁session,keep表示session会被保存。但是在请求中对session的修改会被忽略,也不会保存
//如果用户指定了unset,但是unset不是destroy/keep,那么保存 if (options.unset && options.unset !== 'destroy' && options.unset !== 'keep') { throw new typeerror('unset option must be "destroy" or "keep"'); } // todo: switch to "destroy" on next major var unsetdestroy = options.unset === 'destroy'; // determine if session should be destroyed //sessionid还存在,但是req.session已经被销毁了 function shoulddestroy(req) { // var unsetdestroy = options.unset === 'destroy'; return req.sessionid && unsetdestroy && req.session == null; }
我们可以看到unset只能是默认的destroy或者keep,其用于判断是否应该销毁session,如果指定了unset方法为destrory,那么就会销毁session,也就是把req.session设置为null
在版本1.5.0后,cookie-parser这个中间件已经不是express-session工作必须的了。这个模块可以直接对req/res中的cookie进行读写,使用cookie-parser可能导致一些问题,特别是当secret在两个模块之间存在不一致的时候。
请把secure设置为true,这是明智的。但是这需要网站的支持,因为secure需要https的协议。如果设置了secure,但是你使用http访问,那么cookie不会被设置,如果node.js运行在代理上,同时使用了secure:true那么在express中需要设置”信任代理“。
var app = express() app.set('trust proxy', 1) // trust first proxy app.use(session({ secret: 'keyboard cat', resave: false, saveuninitialized: true, cookie: { secure: true } }))
如果在生产环境下需要使用安全的cookit,同时在测试环境也要能够使用。那么可以使用express中的node_env参数
var app = express() var sess = { secret: 'keyboard cat', cookie: {} } if (app.get('env') === 'production') { app.set('trust proxy', 1) // trust first proxy sess.cookie.secure = true // serve secure cookies } app.use(session(sess))
cookie的secure属性可以设置为auto,那么会按照请求的方式来判断,如果是安全的就是secure。但是如果网站同时支持http和https,这时候通过https设置的cookie
对于http是不可见的。这在express的”trust proxy“(简化开发和生产环境)正确设置的情况下特别有用。默认下:cookie.maxage为null
这意味着,浏览器关闭了这个cookie也就过期了。
req.session:
// use the session middleware app.use(session({ secret: 'keyboard cat', cookie: { maxage: 60000 }})) // access the session as req.session app.get('/', function(req, res, next) { var sess = req.session//用这个属性获取session中保存的数据,而且返回的json数据 if (sess.views) { sess.views++ res.setheader('content-type', 'text/html') res.write('<p>views: ' + sess.views + '</p>') res.write('<p>expires in: ' + (sess.cookie.maxage / 1000) + 's</p>') res.end() } else { sess.views = 1 res.end('welcome to the session demo. refresh!') } })
其中req.session是一个session对象,格式如下:
session: //req.session域下面保存的是一个session实例,其中有cookie表示是一个对象 session { //这里是req.session.cookie是一个cookie实例 cookie: { path: '/', _expires: fri may 06 2016 15:44:48 gmt+0800 (中国标准时间), originalmaxage: 2591999960, httponly: true }, flash: { error: [object] } }
session.regenerate():
产生一个session,调用这个方法那么一个新的sid和session实例就会被创建,同时放置在req.session中。但是第一步是销毁指定的session
store.prototype.regenerate = function(req, fn){ var self = this; //regenerate底层调用的是destroy方法,第一个参数是req.sessionid,至于回调中的self.generate必须是对容器进行指定的 this.destroy(req.sessionid, function(err){ self.generate(req); fn(err);//最后回调fn }); //调用这个store的destory方法,销毁req.sessionid,销毁成功后通过刚才的store的generate方法产生一个sessionid };
这时通用store提供的regenerate方法,但是generate方法一般要特定的库进行辅助:
store.generate = function(req){ req.sessionid = generateid(req); req.session = new session(req); req.session.cookie = new cookie(cookieoptions); //用户指定的secure参数如果是auto,那么修改req.session.cookie的secure参数,并通过issecure来判断 if (cookieoptions.secure === 'auto') { req.session.cookie.secure = issecure(req, trustproxy); } };
这时为express-session为store指定的generate方法
session.destory():
销毁session,同时在req.session中被移除,但是在下一次请求的时候又会被创建
req.session.destroy(function(err) { // cannot access session here })
session.reload():
重新装载session中的数据
req.session.reload(function(err) { // session updated })
session.save():
把session中的数据重新保存到store中,用内存的内容去替换掉store中的内容。这个方法在http的响应后自动被调用。如果session中的数据被改变了(这个行为可以通过中间件的很多的配置来改变),正因为如此这个方法一般不用显示调用。但是在长连接的websocket中这个方法一般需要手动调用
req.session.save(function(err) { // session saved })
session.touch():
更新maxage属性,一般不需要手动调用,因为session的中间件已经替你调用了。我们看看session是如何实现这个方法
function session(req, data) { object.defineproperty(this, 'req', { value: req }); object.defineproperty(this, 'id', { value: req.sessionid }); if (typeof data === 'object' && data !== null) { // merge data into this, ignoring prototype properties for (var prop in data) { if (!(prop in this)) { this[prop] = data[prop] } } } } //重置".cookie.maxage"防止在session仍然存活的时候cookie已经过期了 definemethod(session.prototype, 'touch', function touch() { return this.resetmaxage(); }); //resetmaxage方法,用于为cookie的maxage指定为cookie的originalmaxage definemethod(session.prototype, 'resetmaxage', function resetmaxage() { this.cookie.maxage = this.cookie.originalmaxage; return this; });
也就是把session的maxage设置为构造session对象的时候的初始值。
req.session.id:
唯一的,而且不会被改变。我们看看session的构造函数就明白了:
function session(req, data) { object.defineproperty(this, 'req', { value: req }); object.defineproperty(this, 'id', { value: req.sessionid }); if (typeof data === 'object' && data !== null) { // merge data into this, ignoring prototype properties for (var prop in data) { if (!(prop in this)) { this[prop] = data[prop] } } } }
其中defineproperty方法如下:
//重写了object对象的defineproperty,其中defineproperty用于为这个对象指定一个函数,其中第二个参数是函数的名称,第三个是函数本身 function definemethod(obj, name, fn) { object.defineproperty(obj, name, { configurable: true, enumerable: false, value: fn, writable: true }); };
其中session的id值就是req.sessionid属性而且enumerable为false,所以在控制台是打印不出来的
req.session.cookie:
每一个session都有一个cookie对象,因此在每一次请求的时候你都可以改变session的cookie。如我们可以通过req.session.cookie.expires设置为false,这时候浏览器关闭cookie就不存在了
cookie.maxage:
req.session.cookie.maxage返回这个cookie剩余的毫秒数,当然我们也可以通过设置expires来完成
var hour = 3600000 req.session.cookie.expires = new date(date.now() + hour) req.session.cookie.maxage = hour//和上面的expires等价
当maxage设置为60000,也就是一分钟,这时候如果已经过去了30s,那么maxage就会返回30000(不过要等到当前请求结束)。如果这时候我们调用req.session.touch(),那么req.session.maxage就成了初始值了60000了
req.sessionid:
只读的属性。每一个session store必须是一个eventemitter对象,同时要实现特定的方法。我们看看memorystore把:
function memorystore() { store.call(this) this.sessions = object.create(null) } //继承了store中的所有的原型属性 util.inherits(memorystore, store)
也就是说memorystore继承了通用的store的所有的属性和方法,如regenerate,load,createsession,当然也实现了很多自己的方法如all,clear,destroy,get,length,set,touch等
下面讨论的是一些其他的方法:
required方法表示:在这个store上一定会调用的方法
recommended方法表示如果有这个方法那么在这个store上就会调用。optional方法表示不会调用,但是为了给用户一个统一的store!
store.destroy(sid, callback)
必须的方法。通过sessionid来销毁session,如果session已经被销毁,那么回调函数被调用,同时传入一个error对象
store.get(sid, callback)
必须的方法。通过sessionid从store中获取session。回调函数是callback(err,session)。如果session存在那么第二个参数就是session,否则第二个参数就是null/undefined。如果error.code==="enoent"那么回调为callback(null,null)
store.set(sid, session, callback)
必须的方法。如果被成功设置了那么回调为callback(error)
store.touch(sid, session, callback)
推荐的方法。通过一个指定的sid和session对象去”接触“这个session.如果接触到了那么回调为callback(error)。session store用这个方法去删除那些空闲的session。同时这个方法也会通知session store指定的session是活动态的。memorystore实现了这个方法:
//通过指定的sessionid获取当前的session对象,然后把这个对象的cookie更新为新的session对应的cookie,同时sessions中的当前session也进行更新(包括过期时间等) memorystore.prototype.touch = function touch(sessionid, session, callback) { var currentsession = getsession.call(this, sessionid) if (currentsession) { // update expiration currentsession.cookie = session.cookie this.sessions[sessionid] = json.stringify(currentsession) } callback && defer(callback) }
store.length(callback)
可选的方法。获取store中所有的session的个数,回调函数为callback(error,length)
store.clear(callback)
可选的方法,从store中吧所有的session都删除,回调函数为callback(err)
store.all(callback)
可选的方法。以一个数组的方法获取store中的sessions。callback(error,sessions)
session({ secret: settings.cookiesecret, //blog=s%3aisa3_m-vso0l_ghvunpb8kw9dohpccbj.ov7p42pl91um3jueajatpzdlij%2bilgxwod8hmbsluso //其中secret如果是一个string,那么就是用这个string对sessionid对应的cookie进行签名,如果是一个数组那么只有第一个用于签名,其他用于浏览器请求后的验证 key: settings.db, //设置的cookie的名字,从上面可以看到这里指定的是blog,所以浏览器的请求中可以看到这里的sessionid已经不是sessionid了,而是这里的blog name:"qinliang",//name的优先级比key要高,如果同时设置了那么就是按照name来制定的 //没有name时候response中为:set-cookie:blog=s%3a6ojewycwvmmtgxczqawrw0hnlotjkykm.0slax72tmfw%2b4tiit3ox7naj5s6rpwvmur6sy02l0de; path=/; expires=thu, 28 apr 2016 10:47:13 gmt; httponly //当有name的时候resopnse中:set-cookie:qinliang=s%3abdojujvhv0dh9atax_gl4dgz4-1rgvjq.oeuddoralzb4ismuhce8omziad4ig7jut1rezgcycdg; path=/; expires=thu, 28 apr 2016 10:48:26 gmt; httponly resave:true,//没有实现touch方法,同时也设置了session的过期时间为30天 rolling:true,//如果设置了rolling为true,同时saveuninitialized为true,那么每一个请求都会发送没有初始化的session! saveuninitialized:false,//设置为true,存储空间浪费,不允许权限管理 cookie: { maxage: 1000 * 60 * 60 * 24 * 30 }, //cookie里面全部的设置都是对于sessionid的属性的设置,默认的属性为{ path: '/', httponly: true, secure: false, maxage: null }. //所以最后我们保存到数据库里面的信息就是:{"cookie":{"originalmaxage":2592000000,"expires":"2016-04-27t02:30:51.713z","httponly":true,"path":"/"},"flash":{}} store: new mongostore({ db: settings.db, host: settings.host, port: settings.port }) })
从源码的角度来分析配置项:
(1)这里面的secret到底有什么用呢?
我们看看这个express-session到底是如何做的?
function unsigncookie(val, secrets) { for (var i = 0; i < secrets.length; i++) { var result = signature.unsign(val, secrets[i]); if (result !== false) { return result; } } return false; }
这里是通过cookie-signature进行的解密操作
// var cookieid = req.sessionid = getcookie(req, name, secrets); function getcookie(req, name, secrets) { var header = req.headers.cookie; var raw; var val; // read from cookie header if (header) { var cookies = cookie.parse(header); raw = cookies[name]; if (raw) { if (raw.substr(0, 2) === 's:') { //切割掉前面的字符"s:"! val = unsigncookie(raw.slice(2), secrets); //val表示false意味着客户端传递过来的cookie被篡改了! if (val === false) { debug('cookie signature invalid'); val = undefined; } } else { debug('cookie unsigned') } } } // back-compat read from cookieparser() signedcookies data //如果从req.headers.cookie中没有读取到session id的数据,那么就去cookie parser的req.signedcookies中读取 if (!val && req.signedcookies) { val = req.signedcookies[name]; if (val) { deprecate('cookie should be available in req.headers.cookie'); } } // back-compat read from cookieparser() cookies data //如果req.signedcookies中也没有获取到数据那么直接从req.cookies中获取 if (!val && req.cookies) { raw = req.cookies[name]; if (raw) { if (raw.substr(0, 2) === 's:') { val = unsigncookie(raw.slice(2), secrets); if (val) { deprecate('cookie should be available in req.headers.cookie'); } if (val === false) { debug('cookie signature invalid'); val = undefined; } } else { debug('cookie unsigned') } } } return val; }
通过这里我们很容易看到对于session id的获取就是通过上面的secret进行签名的,如果获取到的sessionid已经被修改过,那么表示这个session已经无效了。首先是从req.headers.cookie中获取,然后从req.signedcookies中获取,最后从req.cookies中进行获取!
(2)cookie字段有什么用的?
var session = require('./session/session') , memorystore = require('./session/memory') , cookie = require('./session/cookie') , store = require('./session/store') var cookieoptions = options.cookie || {}; function generatesessionid(sess) { return uid(24); } // generates the new session store.generate = function(req){ req.sessionid = generateid(req);//产生一个sessionid req.session = new session(req);//产生一个session req.session.cookie = new cookie(cookieoptions);//在req.session对象的cookie域下面保存的是一个cookie对象 if (cookieoptions.secure === 'auto') { req.session.cookie.secure = issecure(req, trustproxy); } };
我们看看cookie字段在哪里被处理了:
var cookie = module.exports = function cookie(options) { this.path = '/'; this.maxage = null; this.httponly = true; //最终的this就是这个新创建的cookie具有这些默认的属性,同时还具有用户自己传入的options参数,如用户传入的var cookieoptions = options.cookie || {}; //也就是用户传入的options.cookie属性 if (options) merge(this, options); /*这个utils.merge的源码只有一句话: exports = module.exports = function(a, b){ if (a && b) { for (var key in b) { a[key] = b[key]; } } return a; };*/ this.originalmaxage = undefined == this.originalmaxage ? this.maxage : this.originalmaxage; //默认的originalmaxage就是this.maxage也就是null,如果指定了originalmaxage那么就是用户指定的值 };
也就是说我们在session中传入的cookie参数也成为新创建的cookie的一个属性了,而且这个这个新创建的cookie被保存到req.session.cookie下。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。