Express 프레임워크의express-session 플러그인 공방전
httpVersionMajor: 1,
httpVersionMinor: 1,
httpVersion: '1.1',
complete: true,
headers:{},
rawHeaders:[],
trailers: {},
rawTrailers: [],
upgrade: false,
url: '/',
method: 'GET',
statusCode: null,
statusMessage: null,
baseUrl: '',
originalUrl: '/',
params: {},
//req.params
query: { page: 1, limit: 10 },
//req.query
body: {},
files: {},
secret: undefined,
cookies:
{ qinliang: 's:BDOjujVhV0DH9Atax_gl4DgZ4-1RGvjQ.OeUddoRalzB4iSmUHcE8oMziad4Ig
7jUT1REzGcYcdg',
blog: 's:-ZkSm8urr8KsXAKsZbSTCp8EWOu7zq2o.Axjo6YmD2dLPGQK9aD1mR8FcpOzyHaGG6
cfGUWUVK00' },
signedCookies: {},
//sessionStore MongoStore
sessionStore:
MongoStore {
db:
Db {
domain: null,
recordQueryStats: false,
retryMiliSeconds: 1000,
numberOfRetries: 60,
readPreference: undefined
},
db_collection_name: 'sessions',
defaultExpirationTime: 1209600000,
generate: [Function],
collection:
Collection {
db: [Object],
collectionName: 'sessions',
internalHint: null,
opts: {},
slaveOk: false,
serializeFunctions: false,
raw: false,
readPreference: 'primary',
pkFactory: [Object],
serverCapabilities: undefined
}
},
sessionID: '-ZkSm8urr8KsXAKsZbSTCp8EWOu7zq2o',
//req.sessionID 32
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]
}
}
두 번째 단계: 세션을 저장하는 흔한 메모리 스토어를 살펴보자. 세션을 어떻게 조직하는지 살펴보자. 그러나 공부하기 전에 유니버설 스토어를 먼저 알아야 한다.
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
};
이것은 통용되는store 대상이기 때문에 그는regenerate 방법을 제공했다. 이 방법은 먼저destroy 방법으로 지정한sesison을 소각한 다음에 리셋에서generate를 통해 새로운session 대상을 생성하고 저장한다. 이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 함수입니다.방금 destroy 방법에 대해 말씀드렸으니 Memory Store에서 어떻게 이 destroy 방법을 실현했는지 살펴보겠습니다. //destroy sessionId session, MemoryStore sessions sessionId session
MemoryStore.prototype.destroy = function destroy(sessionId, callback) {
delete this.sessions[sessionId]
callback && defer(callback)
}
분명히 Memory Store는 지정한 세션을 세션 집합에서 삭제하면 됩니다!일반적인store는 지정한sessionID를 불러오는 데 사용할load 방법을 제공합니다:
// 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);
});
};
분명히 여기에createSession 방법이 하나 있다. 우선 그의 코드를 보고 나서 이야기하자.// 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;
};
분명히 이 방법은session 실례를 만드는 데 사용되지만session 실례를 만들기 전에 쿠키를 만들어야 합니다. 이것은express의 내부 데이터가 왜 이렇습니까? 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]
}
}
쿠키를 만든 후에 세션을 어떻게 만들었는지 코드가 있습니다. 세션이 어떻게 만들었는지 봅시다.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]
}
}
}
}
분명히 우리가 만든session의 실례는 요청 대상을 표시하는 req 속성이 있고 또 하나의 id 옵션은session ID를 표시하는 동시에 두 번째 파라미터의 모든 속성도 이session에 봉인되어 있다. 최종적으로session 아래에 쿠키 필드가 하나 더 생겼다. 그러면 req와 id 속성이 왜 처음에 인쇄되지 않았는지 물어볼 수도 있다. 사실은session이 defineProperty 방법을 다시 썼기 때문이다.function defineMethod(obj, name, fn) {
Object.defineProperty(obj, name, {
configurable: true,
enumerable: false,
value: fn,
writable: true
});
};
즉,enumberale를false로 설정한 것이다.우리는 위에서 말한 load 방법을 계속했다. load 방법에서 get 방법을 사용했지만 우리가 통용하는 store에서 get 방법을 지정하지 않았다. Memory Store가 어떻게 실현되었는지 살펴보자. //get sessionId session, callback , null, session !
MemoryStore.prototype.get = function get(sessionId, callback) {
defer(callback, null, getSession.call(this, sessionId))
}
get 방법을 통해load 함수 리셋 중의sess는 지정한sessionID를 통해 조회된session 대상임을 알 수 있다. 우리가load 방법 코드를 다시 한 번 쓰는 것을 분석하기 편리하다.// 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);
});
};
여기에는 방재의createSession 방법으로 돌아가는 것을 보았습니다. 전송된 매개 변수는 {sessionID:sid,sessionStore:self}이고 두 번째 매개 변수는 우리가 지정한sessionId에 따라 조회한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]
}
}
}
}
따라서load 방법이 호출된 후session의req와 id 속성은 변화가 없고 원래의 값입니다.load의 리셋 함수에 대해 말하자면 두 번째 매개 변수는 진정한session이다.그러니까 l oad는 세션에서 지정한 세션 ID를 다시 읽는 세션에만 사용됩니다!우리는 통용되는session 코드를 붙여서 흥미가 있으면 다음과 같이 볼 수 있습니다.'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;
};
세 번째 단계: 방금 통용되는 스토어에 대해 얘기했는데 이제 Memory Store라는 저장된 세션 대상을 분석해 봅시다. // defer , setImeditate, 。 setImmediate node.js
var defer = typeof setImmediate === 'function'
? setImmediate
: function(fn){ process.nextTick(fn.bind.apply(fn, arguments)) }
module.exports = MemoryStore
// MemoryStore, session {}, Store
// MemoryStore, session {}, Store ,load,createSession
function MemoryStore() {
Store.call(this)
this.sessions = Object.create(null)
}
// Store
util.inherits(MemoryStore, Store)
이 라이브러리는 위의 통용되는store를 계승하고 봉인된 집합sessions는 모든session을 저장하는 데 사용됩니다!위의 코드를 붙여 넣기 전에 다음과 같은 호출 방식을 반드시 똑똑히 보아야 한다.function test(fn){
// "qinlang",klfang""
setTimeout(fn.bind.apply(fn, arguments),1000)
}
test(function(){console.log(this);console.log(arguments);},"qinliang","klfang");
// [Function]
//{ '0': 'qinliang', '1': 'klfang' }
MemoryStore에서 제공하는 첫 번째 매개변수 all:// Session
MemoryStore.prototype.all = function all(callback) {
//MemoryStore sessions session
var sessionIds = Object.keys(this.sessions)
var sessions = Object.create(null)
for (var i = 0; i < sessionIds.length; i++) {
var sessionId = sessionIds[i]
// getSession , this MemoryStore , sesisonID
var session = getSession.call(this, sessionId)
// session
if (session) {
sessions[sessionId] = session;
}
}
// defer sessions
callback && defer(callback, null, sessions)
}
이 방법은 모든session 집합을 가져옵니다. 리셋 함수의 첫 번째 파라미터는 오류 정보이고, 두 번째 파라미터는 모든session 집합을 가져옵니다.clear 방법은 모든session을 제거합니다:
// session , callback session callback, !
MemoryStore.prototype.clear = function clear(callback) {
this.sessions = Object.create(null)
callback && defer(callback)
}
destroy 방법은 지정한session을 제거하는 데 사용 //destroy sessionId session, MemoryStore sessions sessionId session
MemoryStore.prototype.destroy = function destroy(sessionId, callback) {
delete this.sessions[sessionId]
callback && defer(callback)
}
get 방법은 지정한sessionID에 대응하는sesison을 가져옵니다 //get sessionId session, callback , null, session !
MemoryStore.prototype.get = function get(sessionId, callback) {
defer(callback, null, getSession.call(this, sessionId))
}
length 방법으로 활동하는session의 수량을 획득// session , MemoryStore all , , session
MemoryStore.prototype.length = function length(callback) {
this.all(function (err, sessions) {
if (err) return callback(err)
callback(null, Object.keys(sessions).length)
})
}
set 방법은 지정한 세션 ID에 대응하는 세션 업데이트에 사용// sessionId session , session!
MemoryStore.prototype.set = function set(sessionId, session, callback) {
this.sessions[sessionId] = JSON.stringify(session)
callback && defer(callback)
}
터치 방법은 현재session의 쿠키를 새로운 쿠키로 설정하고sessions 집합을 업데이트하는 데 사용됩니다!// 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)
}
마지막으로 getSession 방법은 지정한sessionid에 대응하는sesison을 가져오고 이session을 JSON 대상으로 전환합니다. 이session이 만료되면 이session을 삭제하고 되돌려줍니다. 그렇지 않으면 얻은session 대상으로 되돌려줍니다.// sessionId session
function getSession(sessionId) {
var sess = this.sessions[sessionId]
if (!sess) {
return
}
// parse
sess = JSON.parse(sess)
// session
var expires = typeof sess.cookie.expires === 'string'
? new Date(sess.cookie.expires)
: sess.cookie.expires
// session.cookie.expires Date ( string , JSON.parse )
// destroy expired session
if (expires && expires <= Date.now()) {
delete this.sessions[sessionId]
return
}
// session
return sess
}
우리는 Memory Store의 코드를 붙여서 연구할 만한 흥미가 있다.'use strict';
var Store = require('./store')
var util = require('util')
// defer , setImeditate, 。 setImmediate node.js
var defer = typeof setImmediate === 'function'
? setImmediate
: function(fn){ process.nextTick(fn.bind.apply(fn, arguments)) }
module.exports = MemoryStore
// MemoryStore, session {}, Store
// MemoryStore, session {}, Store ,load,createSession
function MemoryStore() {
Store.call(this)
this.sessions = Object.create(null)
}
// Store
util.inherits(MemoryStore, Store)
// Session
MemoryStore.prototype.all = function all(callback) {
//MemoryStore sessions session
var sessionIds = Object.keys(this.sessions)
var sessions = Object.create(null)
for (var i = 0; i < sessionIds.length; i++) {
var sessionId = sessionIds[i]
// getSession , this MemoryStore , sesisonID
var session = getSession.call(this, sessionId)
// session
if (session) {
sessions[sessionId] = session;
}
}
// defer sessions
callback && defer(callback, null, sessions)
}
// session , callback session callback, !
MemoryStore.prototype.clear = function clear(callback) {
this.sessions = Object.create(null)
callback && defer(callback)
}
//destroy sessionId session, MemoryStore sessions sessionId session
MemoryStore.prototype.destroy = function destroy(sessionId, callback) {
delete this.sessions[sessionId]
callback && defer(callback)
}
//get sessionId session, callback , null, session !
MemoryStore.prototype.get = function get(sessionId, callback) {
defer(callback, null, getSession.call(this, sessionId))
}
// session , MemoryStore all , , session
MemoryStore.prototype.length = function length(callback) {
this.all(function (err, sessions) {
if (err) return callback(err)
callback(null, Object.keys(sessions).length)
})
}
// sessionId session , session!
MemoryStore.prototype.set = function set(sessionId, session, callback) {
this.sessions[sessionId] = JSON.stringify(session)
callback && defer(callback)
}
// 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)
}
// sessionId session
function getSession(sessionId) {
var sess = this.sessions[sessionId]
if (!sess) {
return
}
// parse
sess = JSON.parse(sess)
// session
var expires = typeof sess.cookie.expires === 'string'
? new Date(sess.cookie.expires)
: sess.cookie.expires
// session.cookie.expires Date ( string , JSON.parse )
// destroy expired session
if (expires && expires <= Date.now()) {
delete this.sessions[sessionId]
return
}
// session
return sess
}
네 번째 단계: 쿠키를 연구해 보자. 이 대상은store에서 광범위하게 사용된다. 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]
}
}
여기 분명해요,req.세션에 저장된 것은 하나의 세션 대상이지만 세션 대상에 봉인된 것은 하나의 쿠키 대상입니다. 쿠키 대상이 어떻게 만들어졌는지 봅시다.// 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;
};
여기서 Session에서 쿠키의 생성은 new 쿠키(sess.cookie)를 통해 이루어지고 그 중에서 매개 변수는 모두 쿠키의 모든 기본 속성 위에 봉인되어 사용자가 정의한 속성으로 설정됩니다. //new Cookie(sess.cookie); sess={cookie:{expires:xx,originalMaxAge:xxx}}
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
};
기본 path는/이고 기본 maxAge는null이며 기본 httponly는true이며 기본originalMaxAge는maxAge의 값입니다!우리는 쿠키의 모든 코드를 붙여서 관심 있는 것은 자세히 연구할 수 있다.
'use strict';
var merge = require('utils-merge')
, cookie = require('cookie');
//new Cookie(sess.cookie); sess={cookie:{expires:xx,originalMaxAge:xxx}}
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
};
Cookie.prototype = {
set expires(date) {
this._expires = date;
this.originalMaxAge = this.maxAge;
},
get expires() {
return this._expires;
},
set maxAge(ms) {
this.expires = 'number' == typeof ms
? new Date(Date.now() + ms)
: ms;
},
get maxAge() {
return this.expires instanceof Date
? this.expires.valueOf() - Date.now()
: this.expires;
},
get data() {
return {
originalMaxAge: this.originalMaxAge
, expires: this._expires
, secure: this.secure
, httpOnly: this.httpOnly
, domain: this.domain
, path: this.path
}
},
serialize: function(name, val){
return cookie.serialize(name, val, this.data);
},
toJSON: function(){
return this.data;
}
};
5단계: 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]
}
}
}
}
우리는Session의 구조 부분이session에req속성을 제공하는 것을 보았다. 그 값은 우리가 전송한 첫 번째 파라미터이고 id는 우리가 전송한 첫 번째 파라미터의sesisonID 속성이다.두 번째 파라미터는 우리가 만든session 대상 위에 고스란히 봉인됩니다. 즉, 두 번째 파라미터는 우리의 추가 데이터입니다.주의할 점이 있습니다: 이 라이브러리는 defineProperty 방법을 다시 썼습니다. 모든 id.req 등은 교체할 수 없습니다.// Object defineProperty, defineProperty , ,
function defineMethod(obj, name, fn) {
Object.defineProperty(obj, name, {
configurable: true,
enumerable: false,
value: fn,
writable: true
});
};
첫 번째 방법 resetMaxAge 방법//resetMaxAge , cookie maxAge cookie originalMaxAge
defineMethod(Session.prototype, 'resetMaxAge', function resetMaxAge() {
this.cookie.maxAge = this.cookie.originalMaxAge;
return this;
});
분명히 이 방법은 쿠키의 maxAge 속성을 쿠키의originalMaxAge의 값으로 다시 설정할 것이다두 번째 방법 터치 방법
// ".cookie.maxAge" session cookie
defineMethod(Session.prototype, 'touch', function touch() {
return this.resetMaxAge();
});
내부에서 restMaxAge를 호출하여session이 살아남지 못하게 할 때 쿠키는 이미 지나갔습니다.세 번째 방법. save 방법.
// session save , fn(err)
defineMethod(Session.prototype, 'save', function save(fn) {
this.req.sessionStore.set(this.id, this, fn || function(){});
return this;
});
맨 위에 있는 인쇄 결과에서 리q를 먼저 얻을 수 있습니다.세션 스토어의 대상을 지정한 id의sesison을 현재session으로 설정합니다. 즉session의 기능을 저장합니다.네 번째 방법reload는session을 다시 불러오는 데 사용합니다
defineMethod(Session.prototype, 'reload', function reload(fn) {
var req = this.req
, store = this.req.sessionStore;
store.get(this.id, function(err, sess){
if (err) return fn(err);
if (!sess) return fn(new Error('failed to load session'));
store.createSession(req, sess);
fn();
});
return this;
});
다섯 번째 방법destroy는session을 없애는 데 사용// session , req session , sessionStore destroy
defineMethod(Session.prototype, 'destroy', function destroy(fn) {
delete this.req.session;
this.req.sessionStore.destroy(this.id, fn);
return this;
});
여섯 번째 방법regenerate는 새로운session을 만드는 데 사용// session
defineMethod(Session.prototype, 'regenerate', function regenerate(fn) {
this.req.sessionStore.regenerate(this.req, fn);
return this;
});
6단계: 우리는 우리의 마지막express-session 라이브러리를 연구한다
var Session = require('./session/session')
, MemoryStore = require('./session/memory')
, Cookie = require('./session/cookie')
, Store = require('./session/store')
// environment
var env = process.env.NODE_ENV;
//
exports = module.exports = session;
exports.Store = Store;
exports.Cookie = Cookie;
exports.Session = Session;
exports.MemoryStore = MemoryStore;
var warning = 'Warning: connect.session() MemoryStore is not
'
+ 'designed for a production environment, as it will leak
'
+ 'memory, and will not scale past a single process.';
var defer = typeof setImmediate === 'function'
? setImmediate
: function(fn){ process.nextTick(fn.bind.apply(fn, arguments)) }
대부분 위에서 논의한 내용 var options = options || {}
// name - previously "options.key"
, name = options.name || options.key || 'connect.sid'
// name name, key, name "connect.sid"
, store = options.store || new MemoryStore
//store MemoryStore
, trustProxy = options.proxy
// proxy
, storeReady = true
//storeReady true
, rollingSessions = options.rolling || false;
//rolling false
var cookieOptions = options.cookie || {};
//cookieOptions options cookie
var resaveSession = options.resave;
//resave
var saveUninitializedSession = options.saveUninitialized;
//saveUninitialized
var secret = options.secret;
// secret
var generateId = options.genid || generateSessionId;
// genid sessionID, generateSessionId
if (typeof generateId !== 'function') {
throw new TypeError('genid option must be a function');
}
// resaveSession resaveSession true
if (resaveSession === undefined) {
deprecate('undefined resave option; provide resave option');
resaveSession = true;
}
// saveUninitializedSession saveUninitializedSession true
if (saveUninitializedSession === undefined) {
deprecate('undefined saveUninitialized option; provide saveUninitialized option');
saveUninitializedSession = true;
}
// 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';
//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');
}
// 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);
}
사용자가 정의한 내용을 저장한 다음에 내용을 검사한다. 예를 들어 unset은keep이나destroy 등이어야 하며 생산 환경이면MemoryStore를 사용할 수 없다.// 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);
}
};
이generate 방법은 통용되는store의regenerate 방법에서 호출됩니다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
};
쿠키의 secure 속성을 auto로 설정하면 다음 방법으로 안전한 요청인지 판단합니다function issecure(req, trustProxy) {
// socket is https server
// https true
if (req.connection && req.connection.encrypted) {
return true;
}
// do not trust proxy
// https , trust-proxy false , false
if (trustProxy === false) {
return false;
}
// trustProxy true, secure , secure
// no explicit trust; try req.secure from express
if (trustProxy !== true) {
var secure = req.secure;
return typeof secure === 'boolean'
? secure
: false;
}
// read the proto from x-forwarded-proto header
var header = req.headers['x-forwarded-proto'] || '';
var index = header.indexOf(',');
var proto = index !== -1
// x-forwarded-proto , , ,
? header.substr(0, index).toLowerCase().trim()
: header.toLowerCase().trim()
// x-forwarded-proto https true false
return proto === 'https';
}
우선req를 판단한다.connection.encrypted가true라면true로 되돌아오기;proxy를false로 명확하게 지정하면false를 되돌려줍니다. 그렇지 않으면req에서.secure로 판단하기;마지막으로headers ['x-forwarded-proto'] 판단.그중req.connection 속성은 다음과 같은 Socket 인스턴스를 저장합니다.Socket {
_connecting: false,
_hadError: false,
_handle:
TCP {
_externalStream: {},
fd: -1,
reading: true,
owner: [Circular],
onread: [Function: onread],
onconnection: null,
writeQueueSize: 0 },
_parent: null,
_host: null,
_readableState:
ReadableState {
objectMode: false,
highWaterMark: 16384,
buffer: [],
length: 0,
pipes: null,
pipesCount: 0,
flowing: true,
ended: false,
endEmitted: false,
reading: true,
sync: false,
needReadable: true,
emittedReadable: false,
readableListening: false,
resumeScheduled: false,
defaultEncoding: 'utf8',
ranOut: false,
awaitDrain: 0,
readingMore: false,
decoder: null,
encoding: null },
readable: true,
domain: null,
_events:
{ end: [ [Object], [Function: socketOnEnd] ],
finish: [Function: onSocketFinish],
_socketEnd: [Function: onSocketEnd],
drain: [ [Function: ondrain], [Function: socketOnDrain] ],
timeout: [Function],
error: [ [Function: socketOnError], [Function: onevent] ],
close:
[ [Function: serverSocketCloseListener],
[Function: onServerResponseClose],
[Function: onevent] ],
data: [Function: socketOnData],
resume: [Function: onSocketResume],
pause: [Function: onSocketPause] },
_eventsCount: 10,
_maxListeners: undefined,
_writableState:
WritableState {
objectMode: false,
highWaterMark: 16384,
needDrain: false,
ending: false,
ended: false,
finished: false,
decodeStrings: false,
defaultEncoding: 'utf8',
length: 0,
writing: false,
corked: 0,
sync: true,
bufferProcessing: false,
onwrite: [Function],
writecb: null,
writelen: 0,
bufferedRequest: null,
lastBufferedRequest: null,
pendingcb: 0,
prefinished: false,
errorEmitted: false,
bufferedRequestCount: 0,
corkedRequestsFree: CorkedRequest { next: [Object], entry: null, finish: [Function] } },
writable: true,
allowHalfOpen: true,
destroyed: false,
bytesRead: 0,
_bytesDispatched: 0,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server:
Server {
domain: null,
_events:
{ request: [Object],
connection: [Function: connectionListener],
clientError: [Function] },
_eventsCount: 3,
_maxListeners: undefined,
_connections: 3,
_handle:
TCP {
_externalStream: {},
fd: -1,
reading: false,
owner: [Circular],
onread: null,
onconnection: [Function: onconnection],
writeQueueSize: 0 },
_usingSlaves: false,
_slaves: [],
_unref: false,
allowHalfOpen: true,
pauseOnConnect: false,
httpAllowHalfOpen: false,
timeout: 120000,
_pendingResponseData: 0,
_connectionKey: '6::::3000' },
_server:
Server {
domain: null,
_events:
{ request: [Object],
connection: [Function: connectionListener],
clientError: [Function] },
_eventsCount: 3,
_maxListeners: undefined,
_connections: 3,
_handle:
TCP {
_externalStream: {},
fd: -1,
reading: false,
owner: [Circular],
onread: null,
onconnection: [Function: onconnection],
writeQueueSize: 0 },
_usingSlaves: false,
_slaves: [],
_unref: false,
allowHalfOpen: true,
pauseOnConnect: false,
httpAllowHalfOpen: false,
timeout: 120000,
_pendingResponseData: 0,
_connectionKey: '6::::3000' },
_idleTimeout: 120000,
_idleNext:
{ _idleNext:
Socket {
_connecting: false,
_hadError: false,
_handle: [Object],
_parent: null,
_host: null,
_readableState: [Object],
readable: true,
domain: null,
_events: [Object],
_eventsCount: 10,
_maxListeners: undefined,
_writableState: [Object],
writable: true,
allowHalfOpen: true,
destroyed: false,
bytesRead: 0,
_bytesDispatched: 0,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: [Object],
_server: [Object],
_idleTimeout: 120000,
_idleNext: [Object],
_idlePrev: [Circular],
_idleStart: 5397,
parser: [Object],
on: [Function: socketOnWrap],
_paused: false,
read: [Function],
_consuming: true },
_idlePrev: [Circular] },
_idlePrev:
Socket {
_connecting: false,
_hadError: false,
_handle:
TCP {
_externalStream: {},
fd: -1,
reading: true,
owner: [Circular],
onread: [Function: onread],
onconnection: null,
writeQueueSize: 0 },
_parent: null,
_host: null,
_readableState:
ReadableState {
objectMode: false,
highWaterMark: 16384,
buffer: [],
length: 0,
pipes: null,
pipesCount: 0,
flowing: true,
ended: false,
endEmitted: false,
reading: true,
sync: false,
needReadable: true,
emittedReadable: false,
readableListening: false,
resumeScheduled: false,
defaultEncoding: 'utf8',
ranOut: false,
awaitDrain: 0,
readingMore: false,
decoder: null,
encoding: null },
readable: true,
domain: null,
_events:
{ end: [Object],
finish: [Function: onSocketFinish],
_socketEnd: [Function: onSocketEnd],
drain: [Object],
timeout: [Function],
error: [Function: socketOnError],
close: [Function: serverSocketCloseListener],
data: [Function: socketOnData],
resume: [Function: onSocketResume],
pause: [Function: onSocketPause] },
_eventsCount: 10,
_maxListeners: undefined,
_writableState:
WritableState {
objectMode: false,
highWaterMark: 16384,
needDrain: false,
ending: false,
ended: false,
finished: false,
decodeStrings: false,
defaultEncoding: 'utf8',
length: 0,
writing: false,
corked: 0,
sync: true,
bufferProcessing: false,
onwrite: [Function],
writecb: null,
writelen: 0,
bufferedRequest: null,
lastBufferedRequest: null,
pendingcb: 0,
prefinished: false,
errorEmitted: false,
bufferedRequestCount: 0,
corkedRequestsFree: [Object] },
writable: true,
allowHalfOpen: true,
destroyed: false,
bytesRead: 0,
_bytesDispatched: 0,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server:
Server {
domain: null,
_events: [Object],
_eventsCount: 3,
_maxListeners: undefined,
_connections: 3,
_handle: [Object],
_usingSlaves: false,
_slaves: [],
_unref: false,
allowHalfOpen: true,
pauseOnConnect: false,
httpAllowHalfOpen: false,
timeout: 120000,
_pendingResponseData: 0,
_connectionKey: '6::::3000' },
_server:
Server {
domain: null,
_events: [Object],
_eventsCount: 3,
_maxListeners: undefined,
_connections: 3,
_handle: [Object],
_usingSlaves: false,
_slaves: [],
_unref: false,
allowHalfOpen: true,
pauseOnConnect: false,
httpAllowHalfOpen: false,
timeout: 120000,
_pendingResponseData: 0,
_connectionKey: '6::::3000' },
_idleTimeout: 120000,
_idleNext: [Circular],
_idlePrev:
Socket {
_connecting: false,
_hadError: false,
_handle: [Object],
_parent: null,
_host: null,
_readableState: [Object],
readable: true,
domain: null,
_events: [Object],
_eventsCount: 10,
_maxListeners: undefined,
_writableState: [Object],
writable: true,
allowHalfOpen: true,
destroyed: false,
bytesRead: 0,
_bytesDispatched: 0,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: [Object],
_server: [Object],
_idleTimeout: 120000,
_idleNext: [Circular],
_idlePrev: [Object],
_idleStart: 5397,
parser: [Object],
on: [Function: socketOnWrap],
_paused: false,
read: [Function],
_consuming: true },
_idleStart: 5396,
parser:
HTTPParser {
'0': [Function: parserOnHeaders],
'1': [Function: parserOnHeadersComplete],
'2': [Function: parserOnBody],
'3': [Function: parserOnMessageComplete],
'4': [Function: onParserExecute],
_headers: [],
_url: '',
_consumed: true,
socket: [Circular],
incoming: null,
outgoing: null,
maxHeaderPairs: 2000,
onIncoming: [Function: parserOnIncoming] },
on: [Function: socketOnWrap],
_paused: false,
read: [Function],
_consuming: true },
_idleStart: 5392,
parser:
HTTPParser {
'0': [Function: parserOnHeaders],
'1': [Function: parserOnHeadersComplete],
'2': [Function: parserOnBody],
'3': [Function: parserOnMessageComplete],
'4': [Function: onParserExecute],
_headers: [],
_url: '',
_consumed: true,
socket: [Circular],
incoming:
IncomingMessage {
_readableState: [Object],
readable: true,
domain: null,
_events: {},
_eventsCount: 0,
_maxListeners: undefined,
socket: [Circular],
connection: [Circular],
httpVersionMajor: 1,
httpVersionMinor: 1,
httpVersion: '1.1',
complete: true,
headers: [Object],
rawHeaders: [Object],
trailers: {},
rawTrailers: [],
upgrade: false,
url: '/',
method: 'GET',
statusCode: null,
statusMessage: null,
client: [Circular],
_consuming: false,
_dumped: false,
next: [Function: next],
baseUrl: '',
originalUrl: '/',
_parsedUrl: [Object],
params: {},
query: [Object],
res: [Object],
_startAt: [Object],
_startTime: Thu Apr 07 2016 09:51:10 GMT+0800 ( ),
_remoteAddress: '::1',
body: {},
files: {},
secret: undefined,
cookies: [Object],
signedCookies: {},
_parsedOriginalUrl: [Object],
sessionStore: [Object],
sessionID: '-ZkSm8urr8KsXAKsZbSTCp8EWOu7zq2o',
session: [Object],
flash: [Function: _flash],
offset: 0,
skip: 0,
route: [Object] },
outgoing: null,
maxHeaderPairs: 2000,
onIncoming: [Function: parserOnIncoming] },
on: [Function: socketOnWrap],
_paused: false,
read: [Function],
_consuming: true,
_httpMessage:
ServerResponse {
domain: null,
_events: { finish: [Object], end: [Function: onevent] },
_eventsCount: 2,
_maxListeners: undefined,
output: [],
outputEncodings: [],
outputCallbacks: [],
outputSize: 0,
writable: true,
_last: false,
chunkedEncoding: false,
shouldKeepAlive: true,
useChunkedEncodingByDefault: true,
sendDate: true,
_removedHeader: {},
_contentLength: null,
_hasBody: true,
_trailer: '',
finished: false,
_headerSent: false,
socket: [Circular],
connection: [Circular],
_header: null,
_headers: { 'x-powered-by': 'Express' },
_headerNames: { 'x-powered-by': 'X-Powered-By' },
_onPendingData: [Function: updateOutgoingData],
req:
IncomingMessage {
_readableState: [Object],
readable: true,
domain: null,
_events: {},
_eventsCount: 0,
_maxListeners: undefined,
socket: [Circular],
connection: [Circular],
httpVersionMajor: 1,
httpVersionMinor: 1,
httpVersion: '1.1',
complete: true,
headers: [Object],
rawHeaders: [Object],
trailers: {},
rawTrailers: [],
upgrade: false,
url: '/',
method: 'GET',
statusCode: null,
statusMessage: null,
client: [Circular],
_consuming: false,
_dumped: false,
next: [Function: next],
baseUrl: '',
originalUrl: '/',
_parsedUrl: [Object],
params: {},
query: [Object],
res: [Circular],
_startAt: [Object],
_startTime: Thu Apr 07 2016 09:51:10 GMT+0800 ( ),
_remoteAddress: '::1',
body: {},
files: {},
secret: undefined,
cookies: [Object],
signedCookies: {},
_parsedOriginalUrl: [Object],
sessionStore: [Object],
sessionID: '-ZkSm8urr8KsXAKsZbSTCp8EWOu7zq2o',
session: [Object],
flash: [Function: _flash],
offset: 0,
skip: 0,
route: [Object] },
locals: { paginate: [Object] },
__onFinished: { [Function: listener] queue: [Object] },
writeHead: [Function: writeHead],
end: [Function: end] },
_peername: { address: '::1', family: 'IPv6', port: 51294 } }
Express에서 이 플러그인을 어떻게 호출하는지 알아야 합니다.
app.use(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
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
})
}));
따라서 되돌아오는 것은 중간부품이어야 한다. 아래의 코드를 통해 알 수 있다.// , Express
return function session(req, res, next) {
// self-awareness
// req session , session
if (req.session) return next();
// storeReady false debug ,
// Handle connection as if there is no session if
// the store has temporarily disconnected etc
if (!storeReady) return debug('store is disconnected'), next();
// pathname mismatch
//parseurl URL, req.url 。 Node.js url.parse 。 req
// req.url , , 。 ,parseUrl.original(req) req.originalUrl, req.url
// Node.js url.parse , req.originalUrl
var originalPath = parseUrl.original(req).pathname;
if (originalPath.indexOf(cookieOptions.path || '/') !== 0) return next();
// URL cookie path
// ensure a secret is available or bail
if (!secret && !req.secret) {
next(new Error('secret option required for sessions'));
return;
}
// backwards compatibility for signed cookies
// req.secret is passed from the cookie parser middleware
var secrets = secret || [req.secret];
// secret req.secret , cookie parser signed cookie
var originalHash;
var originalId;
var savedHash;
// expose store
req.sessionStore = store;
// sessionStore store , options store , MemoryStore
// get the session ID from the cookie
var cookieId = req.sessionID = getcookie(req, name, secrets);
// req sessionID , getcookie , name :name = options.name || options.key || 'connect.sid' sessionID
// set-cookie
//onHeaders(res, listener) listener headers res , listener res this 。headers ,
// res onHeaders , listener
onHeaders(res, function(){
// session
if (!req.session) {
debug('no session');
return;
}
// session cookie
var cookie = req.session.cookie;
// cookie secure https ,
// only send secure cookies via https
if (cookie.secure && !issecure(req, trustProxy)) {
debug('not secured');
return;
}
// cookie
if (!shouldSetCookie(req)) {
return;
}
// cookie,
setcookie(res, name, req.sessionID, secrets[0], cookie.data);
});
// proxy end() to commit the session
var _end = res.end;
var _write = res.write;
var ended = false;
// res.end , chunk encoding
res.end = function end(chunk, encoding) {
if (ended) {
return false;
}
// ended true
ended = true;
var ret;
var sync = true;
//writeend , end , end this res , chunk, encoding
function writeend() {
if (sync) {
ret = _end.call(res, chunk, encoding);
sync = false;
return;
}
// sync false end
_end.call(res);
}
//writetop
function writetop() {
if (!sync) {
return ret;
}
if (chunk == null) {
ret = true;
return ret;
}
// Content-Length
var contentLength = Number(res.getHeader('Content-Length'));
//
if (!isNaN(contentLength) && contentLength > 0) {
// measure chunk
chunk = !Buffer.isBuffer(chunk)
? new Buffer(chunk, encoding)
: chunk;
// chunk Buffer
encoding = undefined;
if (chunk.length !== 0) {
debug('split response');
// write
ret = _write.call(res, chunk.slice(0, chunk.length - 1));
// chunk
chunk = chunk.slice(chunk.length - 1, chunk.length);
return ret;
}
}
// write
ret = _write.call(res, chunk, encoding);
sync = false;
return ret;
}
if (shouldDestroy(req)) {
// destroy session
debug('destroying');
// session store destroy , destrory req.sessionID
store.destroy(req.sessionID, function ondestroy(err) {
if (err) {
//var defer = typeof setImmediate === 'function'? setImmediate: function(fn){ process.nextTick(fn.bind.apply(fn, arguments)) }
//defer setImmediate , setImmediate 。
defer(next, err);
}
debug('destroyed');
writeend();// writeEnd
});
// writetop
return writetop();
}
// no session to save
// session
if (!req.session) {
debug('no session');
return _end.call(res, chunk, encoding);
}
// req.session touch
// touch session
req.session.touch();
//
if (shouldSave(req)) {
req.session.save(function onsave(err) {
if (err) {
defer(next, err);
}
writeend();
});
return writetop();
} else if (storeImplementsTouch && shouldTouch(req)) {
// store implements touch method
debug('touching');
// touch
store.touch(req.sessionID, req.session, function ontouch(err) {
if (err) {
defer(next, err);
}
debug('touched');
writeend();
});
return writetop();
}
return _end.call(res, chunk, encoding);
};
위의 코드를 자세히 분석해 봅시다. // self-awareness
// req session , session
if (req.session) return next();
// storeReady false debug ,
// Handle connection as if there is no session if
// the store has temporarily disconnected etc
if (!storeReady) return debug('store is disconnected'), next();
// pathname mismatch
//parseurl URL, req.url 。 Node.js url.parse 。 req
// req.url , , 。 ,parseUrl.original(req) req.originalUrl, req.url
// Node.js url.parse , req.originalUrl
var originalPath = parseUrl.original(req).pathname;
if (originalPath.indexOf(cookieOptions.path || '/') !== 0) return next();
// URL cookie path
session이 있으면 다음 중간부품을 직접 호출합니다.express-sesison이 다음 중간부품을 호출하지 않거나 경로가 일치하지 않으면 다음 중간부품을 호출합니다. // ensure a secret is available or bail
if (!secret && !req.secret) {
next(new Error('secret option required for sessions'));
return;
}
// backwards compatibility for signed cookies
// req.secret is passed from the cookie parser middleware
var secrets = secret || [req.secret];
// secret req.secret , cookie parser signed cookie
사용자의 키 저장 // secret req.secret , cookie parser signed cookie
var originalHash;
var originalId;
var savedHash;
// expose store
req.sessionStore = store;
// sessionStore store , options store , MemoryStore
// get the session ID from the cookie
var cookieId = req.sessionID = getcookie(req, name, secrets);
// req sessionID , getcookie , name :name = options.name || options.key || 'connect.sid' sessionID
// set-cookie
req 대상의sessionStore에session을 저장하고sessionID를 지정합니다. 그 중에서sessionID는 브라우저에서 보내온 것입니다.// var cookieId = req.sessionID = getcookie(req, name, secrets);
// : 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;
}
먼저req.headers.플러그인 쿠키-parser를 사용하면req.signedCookies에서 가져오지 않으면 req에서 직접 가져옵니다.cookies 가져오기 //onHeaders(res, listener) listener headers res , listener res this 。headers ,
// res onHeaders , listener
onHeaders(res, function(){
// session
if (!req.session) {
debug('no session');
return;
}
// session cookie
var cookie = req.session.cookie;
// cookie secure https ,
// only send secure cookies via https
if (cookie.secure && !issecure(req, trustProxy)) {
debug('not secured');
return;
}
// cookie
if (!shouldSetCookie(req)) {
return;
}
// cookie,
setcookie(res, name, req.sessionID, secrets[0], cookie.data);
});
set Cookie가 Req를 어떻게 설정하는지 봅시다.세션 ID의 응답 헤더 설정임을 기억하십시오
// 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)
}
res에서req에 대한sessionID가 암호화되었으며 req.session.cookie.데이터에sessionID라는 쿠키를 제외한 다른 쿠키의 값을 설정해야 합니다.위의 함수는 shouldSetCookie입니다. 쿠키를 브라우저에 저장해야 하는지 판단하는 방법을 살펴보겠습니다. // 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
// sesisonID , session req.session , shouldSetCookie true
// sesionID ,
? saveUninitializedSession || isModified(req.session)
//rollingSessions = options.rolling || false, rolling sessionCookie 。 rolling sessionID
// session cookie
: rollingSessions || req.session.cookie.expires != null && isModified(req.session);
}
isModified가session이 수정되었음을 어떻게 판단하는지 살펴보자. 사실은session의 id 속성을 통해 판단하지만 이 id의enumerable는false이다. // check if session has been modified
// session originalId = req.sessionID; originalHash = hash(req.session);
function isModified(sess) {
return originalId !== sess.id || originalHash !== hash(sess);
}
다음은 이 플러그인의 몇 가지 방법을 살펴보겠습니다.세션이 저장되었는지 판단하는 방법
// check if session has been saved
// session ,originalId = req.sessionID;
function isSaved(sess) {
return originalId === sess.id && savedHash === hash(sess);
}
hash방법을 살펴보면 순환 불필요한 검사 알고리즘을 통해 완성되지만session의 쿠키를 제외하고는// hash key cookie , cookie crc !
// var crc = require('crc');crc.crc32('hello').toString(16);
function hash(sess) {
return crc(JSON.stringify(sess, function (key, val) {
if (key !== 'cookie') {
return val;
}
}));
}
session을 삭제해야 하는지 여부 // session , req.sessionID , unset destrory req.session null
function shouldDestroy(req) {
// var unsetDestroy = options.unset === 'destroy';
return req.sessionID && unsetDestroy && req.session == null;
}
만약에 이req의sessionID가 존재한다면 unset을destroy로 설정하지만req.session이null이면true로 돌아갑니다다음 방법은 사용자가session을 저장해야 하는지 여부를 판단합니다.
// 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);
// saveUninitialized false, cookieId req.sessionID , , ,
return !saveUninitializedSession && cookieId !== req.sessionID
? isModified(req.session)
: !isSaved(req.session)
}
사용자가 초기화되지 않은session을 지정하면 저장할 필요가 없으며req.sesison ID와 요청 중의 쿠키 ID가 일치하지 않으면 req.세션이 수정되어야만 저장할 수 있습니다.그렇지 않으면 저장이 되면 저장이 안 돼요.issaved 방법은 다음과 같습니다.
// session ,originalId = req.sessionID;
function isSaved(sess) {
return originalId === sess.id && savedHash === hash(sess);
}
다음은 Should Touch 방법을 살펴보겠습니다. // determine if session should be touched
function shouldTouch(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;
}
return cookieId === req.sessionID && !shouldSave(req);
}
요청 중인sessionId와req.session ID가 일치하고 shouldSave가false로 돌아오면 shouldTouch가true로 돌아갑니다!기타 내용 분석, 개인 공간 참조
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.