mm_session 1.5.2 → 1.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -12
- package/README_EN.md +45 -12
- package/eslint.config.js +3 -4
- package/index.js +4 -3
- package/lib/helper.js +99 -0
- package/lib/session.js +335 -120
- package/lib/store.js +15 -44
- package/package.json +5 -5
- package/test.js +287 -149
package/lib/session.js
CHANGED
|
@@ -1,28 +1,67 @@
|
|
|
1
1
|
const { Store } = require('./store.js');
|
|
2
|
+
const { Helper } = require('./helper.js');
|
|
3
|
+
|
|
4
|
+
if (!$.dict) {
|
|
5
|
+
// 增加字典
|
|
6
|
+
$.dict = {};
|
|
7
|
+
}
|
|
2
8
|
|
|
3
9
|
/**
|
|
4
10
|
* Session类
|
|
5
11
|
*/
|
|
6
12
|
class Session {
|
|
13
|
+
/**
|
|
14
|
+
* 默认配置
|
|
15
|
+
*/
|
|
16
|
+
static config = {
|
|
17
|
+
// 密钥
|
|
18
|
+
encrypt_key: $.config?.encrypt_key ? $.config.encrypt_key : 'mm'.md5().substring(0, 16),
|
|
19
|
+
// session存储在缓存中的key前缀,默认'mm:uuid'
|
|
20
|
+
key_prefix: $.dict.session_id || 'mm:uuid',
|
|
21
|
+
// session ID存储在cookie中的字段名,默认'mm:uuid'
|
|
22
|
+
cookie_token: $.dict.cookie_token || 'mm:uuid',
|
|
23
|
+
// HTTP请求头中用于传递session ID的字段名,默认'x-auth-token'
|
|
24
|
+
header_token: $.dict.header_token || 'x-auth-token',
|
|
25
|
+
// session ID过期时间,单位秒,默认7天
|
|
26
|
+
uuid_expire: $.dict.uuid_expire || 60 * 60 * 24 * 7,
|
|
27
|
+
// session过期时间,单位秒,默认2小时
|
|
28
|
+
max_age: 60 * 60 * 2,
|
|
29
|
+
// 过期token处理选项:'reject'(拒绝访问)或'allow'(允许访问缓存)
|
|
30
|
+
expired_handling: 'reject',
|
|
31
|
+
};
|
|
32
|
+
|
|
7
33
|
/**
|
|
8
34
|
* 构造函数
|
|
9
35
|
* @param {object} config 配置选项
|
|
10
36
|
*/
|
|
11
37
|
constructor(config = {}) {
|
|
12
|
-
this.config = {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
this._store = new Store();
|
|
38
|
+
this.config = { ...Session.config };
|
|
39
|
+
this.setConfig(config);
|
|
40
|
+
this._init();
|
|
18
41
|
}
|
|
19
42
|
}
|
|
20
43
|
|
|
44
|
+
/**
|
|
45
|
+
* 设置session配置
|
|
46
|
+
* @param {object} config 配置选项
|
|
47
|
+
*/
|
|
48
|
+
Session.prototype.setConfig = function (config) {
|
|
49
|
+
$.push(this.config, config);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 初始化session存储和助手
|
|
54
|
+
*/
|
|
55
|
+
Session.prototype._init = function () {
|
|
56
|
+
this._store = new Store(this.config.key_prefix);
|
|
57
|
+
this._helper = new Helper();
|
|
58
|
+
};
|
|
59
|
+
|
|
21
60
|
/**
|
|
22
61
|
* 初始化session存储
|
|
23
62
|
* @param {Store} store session存储实例
|
|
24
63
|
*/
|
|
25
|
-
Session.prototype.init = function(store) {
|
|
64
|
+
Session.prototype.init = function (store) {
|
|
26
65
|
if (store) {
|
|
27
66
|
this._store = store;
|
|
28
67
|
}
|
|
@@ -32,30 +71,79 @@ Session.prototype.init = function(store) {
|
|
|
32
71
|
* 创建session中间件
|
|
33
72
|
* @returns {Function} 中间件函数
|
|
34
73
|
*/
|
|
35
|
-
Session.prototype.middleware = function() {
|
|
36
|
-
|
|
37
|
-
|
|
74
|
+
Session.prototype.middleware = function () {
|
|
75
|
+
var self = this;
|
|
76
|
+
|
|
77
|
+
return async function (ctx, next) {
|
|
78
|
+
await self._handle(ctx, next);
|
|
38
79
|
};
|
|
39
80
|
};
|
|
40
81
|
|
|
41
82
|
/**
|
|
42
|
-
*
|
|
83
|
+
* 获取session对象
|
|
43
84
|
* @param {object} ctx HTTP上下文
|
|
44
|
-
* @
|
|
85
|
+
* @returns {object} session对象
|
|
45
86
|
*/
|
|
46
|
-
Session.prototype.
|
|
47
|
-
|
|
48
|
-
let
|
|
87
|
+
Session.prototype.get = async function (ctx) {
|
|
88
|
+
// 获取协议头信息
|
|
89
|
+
let { ip, user_agent } = this._getHeaderInfo(ctx);
|
|
90
|
+
// 获取session ID
|
|
91
|
+
let session_id = this.getId(ctx);
|
|
92
|
+
|
|
93
|
+
// 验证session ID标识的有效性
|
|
94
|
+
if (session_id) {
|
|
95
|
+
let is_valid = await this._checkSessionId(session_id, ip, user_agent);
|
|
96
|
+
if (!is_valid) {
|
|
97
|
+
// token验证失败,返回null创建新session
|
|
98
|
+
session_id = null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
49
101
|
|
|
50
|
-
|
|
102
|
+
// 获取session对象
|
|
103
|
+
let session = await this._get(session_id);
|
|
51
104
|
|
|
52
|
-
|
|
105
|
+
if (!session.uuid) {
|
|
106
|
+
session.uuid = this._genId(ip, user_agent);
|
|
107
|
+
}
|
|
108
|
+
// 创建session代理对象
|
|
109
|
+
ctx.session = this._create(session);
|
|
110
|
+
// 追加方法
|
|
111
|
+
this._addMethod(ctx);
|
|
112
|
+
return ctx.session;
|
|
113
|
+
};
|
|
53
114
|
|
|
115
|
+
/**
|
|
116
|
+
* 处理session逻辑
|
|
117
|
+
* @param {object} ctx HTTP上下文
|
|
118
|
+
* @param {Function} next 下一个中间件
|
|
119
|
+
*/
|
|
120
|
+
Session.prototype._handle = async function (ctx, next) {
|
|
121
|
+
console.log(' - _handle方法开始执行');
|
|
122
|
+
let session_id = await this.get(ctx);
|
|
123
|
+
|
|
124
|
+
// 执行业务逻辑(包括登录/退出)
|
|
125
|
+
console.log(' - 执行next()之前');
|
|
54
126
|
await next();
|
|
127
|
+
console.log(' - 执行next()之后');
|
|
55
128
|
|
|
56
|
-
|
|
129
|
+
// 保存session(基于session是否被修改)
|
|
130
|
+
console.log(' - 准备调用save方法');
|
|
131
|
+
await this._save(ctx, session_id);
|
|
132
|
+
console.log(' - save方法执行完成');
|
|
133
|
+
};
|
|
57
134
|
|
|
58
|
-
|
|
135
|
+
/**
|
|
136
|
+
* 获取协议头信息
|
|
137
|
+
* @param {object} ctx HTTP上下文
|
|
138
|
+
* @returns {object} session信息
|
|
139
|
+
*/
|
|
140
|
+
Session.prototype._getHeaderInfo = function (ctx) {
|
|
141
|
+
let ip = ctx.ip || '127.0.0.1';
|
|
142
|
+
let user_agent = ctx.headers['user-agent'] || 'mm';
|
|
143
|
+
return {
|
|
144
|
+
ip,
|
|
145
|
+
user_agent
|
|
146
|
+
};
|
|
59
147
|
};
|
|
60
148
|
|
|
61
149
|
/**
|
|
@@ -63,59 +151,84 @@ Session.prototype._handle = async function(ctx, next) {
|
|
|
63
151
|
* @param {object} ctx HTTP上下文
|
|
64
152
|
* @returns {string} session ID
|
|
65
153
|
*/
|
|
66
|
-
Session.prototype.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
154
|
+
Session.prototype.getId = function (ctx) {
|
|
155
|
+
// 先尝试从cookie中获取session ID
|
|
156
|
+
let session_id = ctx.cookies.get(this.config.cookie_token, this.config.options);
|
|
157
|
+
if (!session_id) {
|
|
158
|
+
// 如果cookie中没有,尝试从header中获取
|
|
159
|
+
session_id = ctx.headers[this.config.header_token];
|
|
70
160
|
}
|
|
71
|
-
return
|
|
161
|
+
return session_id;
|
|
72
162
|
};
|
|
73
163
|
|
|
74
164
|
/**
|
|
75
|
-
*
|
|
76
|
-
* @param {
|
|
77
|
-
* @param {string}
|
|
78
|
-
* @
|
|
79
|
-
* @returns {object} session对象
|
|
165
|
+
* 生成session ID
|
|
166
|
+
* @param {string} ip 客户端IP地址
|
|
167
|
+
* @param {string} user_agent 用户代理字符串
|
|
168
|
+
* @returns {string} 生成的session ID
|
|
80
169
|
*/
|
|
81
|
-
Session.prototype.
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
170
|
+
Session.prototype._genId = function (ip, user_agent) {
|
|
171
|
+
return this._helper.genId(this.config.encrypt_key, ip, user_agent, this.config.uuid_expire);
|
|
172
|
+
};
|
|
85
173
|
|
|
86
|
-
|
|
87
|
-
|
|
174
|
+
/**
|
|
175
|
+
* 生成session ID
|
|
176
|
+
* @param {object} ctx HTTP上下文
|
|
177
|
+
* @returns {string} session ID
|
|
178
|
+
*/
|
|
179
|
+
Session.prototype.genId = function (ctx) {
|
|
180
|
+
let { ip, user_agent } = this._getHeaderInfo(ctx);
|
|
181
|
+
return this._genId(ip, user_agent);
|
|
182
|
+
};
|
|
88
183
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
184
|
+
/**
|
|
185
|
+
* 初始化session
|
|
186
|
+
*
|
|
187
|
+
* @param {string} session_id session ID
|
|
188
|
+
* @returns {object} session对象
|
|
189
|
+
*/
|
|
190
|
+
Session.prototype._get = async function (session_id) {
|
|
191
|
+
let session;
|
|
192
|
+
if (session_id) {
|
|
193
|
+
var data = await this._store.get(session_id);
|
|
194
|
+
// 如果session不存在,创建新session
|
|
195
|
+
if (data) {
|
|
196
|
+
session = data;
|
|
197
|
+
// 现有session,设置标志位
|
|
198
|
+
session._is_new = false;
|
|
199
|
+
session._modified = false;
|
|
200
|
+
}
|
|
92
201
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
202
|
+
if (!session) {
|
|
203
|
+
session = {
|
|
204
|
+
_is_new: true, // 新session标志
|
|
205
|
+
_modified: false // 修改标志
|
|
206
|
+
};
|
|
96
207
|
}
|
|
97
|
-
|
|
98
|
-
|
|
208
|
+
// 设置session的uuid属性
|
|
209
|
+
session.uuid = session_id;
|
|
210
|
+
return session;
|
|
99
211
|
};
|
|
100
212
|
|
|
101
213
|
/**
|
|
102
214
|
* 创建新session
|
|
103
|
-
* @param {object}
|
|
215
|
+
* @param {object} session session对象
|
|
104
216
|
* @returns {Proxy} session代理对象
|
|
105
217
|
*/
|
|
106
|
-
Session.prototype.
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
new_obj.uuid = uuid;
|
|
115
|
-
});
|
|
218
|
+
Session.prototype._create = function (session) {
|
|
219
|
+
// 使用Proxy监听session修改
|
|
220
|
+
return new Proxy(session, {
|
|
221
|
+
set: function (target, property, value) {
|
|
222
|
+
// 内部属性不触发修改标记
|
|
223
|
+
if (property.startsWith('_')) {
|
|
224
|
+
Reflect.set(target, property, value);
|
|
225
|
+
return true;
|
|
116
226
|
}
|
|
117
|
-
|
|
118
|
-
|
|
227
|
+
|
|
228
|
+
Reflect.set(target, property, value);
|
|
229
|
+
// 普通属性被设置时标记为已修改
|
|
230
|
+
Reflect.set(target, '_modified', true);
|
|
231
|
+
return true;
|
|
119
232
|
}
|
|
120
233
|
});
|
|
121
234
|
};
|
|
@@ -123,22 +236,25 @@ Session.prototype._new = function(ctx) {
|
|
|
123
236
|
/**
|
|
124
237
|
* 添加session方法
|
|
125
238
|
* @param {object} ctx HTTP上下文
|
|
126
|
-
* @param {boolean} _need_refresh 是否需要刷新
|
|
127
239
|
*/
|
|
128
|
-
Session.prototype.
|
|
240
|
+
Session.prototype._addMethod = function (ctx) {
|
|
129
241
|
/**
|
|
130
242
|
* 刷新session
|
|
131
243
|
*/
|
|
132
244
|
ctx.session.refresh = () => {
|
|
133
245
|
// 刷新标记在外部处理
|
|
246
|
+
ctx.session._modified = true;
|
|
134
247
|
};
|
|
135
248
|
|
|
136
249
|
/**
|
|
137
250
|
* 获取session ID
|
|
138
251
|
* @returns {string} session ID
|
|
139
252
|
*/
|
|
140
|
-
ctx.session.
|
|
141
|
-
|
|
253
|
+
ctx.session.getId = () => {
|
|
254
|
+
if (!ctx.session.uuid) {
|
|
255
|
+
ctx.session.uuid = this.genId(ctx);
|
|
256
|
+
}
|
|
257
|
+
return ctx.session.uuid;
|
|
142
258
|
};
|
|
143
259
|
};
|
|
144
260
|
|
|
@@ -146,98 +262,150 @@ Session.prototype._add = function(ctx, _need_refresh) {
|
|
|
146
262
|
* 清理session
|
|
147
263
|
* @param {object} ctx HTTP上下文
|
|
148
264
|
*/
|
|
149
|
-
Session.prototype._cleanup = function(ctx) {
|
|
150
|
-
if (ctx.session
|
|
151
|
-
delete ctx.session.
|
|
265
|
+
Session.prototype._cleanup = function (ctx) {
|
|
266
|
+
if (ctx.session) {
|
|
267
|
+
delete ctx.session._is_new;
|
|
268
|
+
delete ctx.session._modified;
|
|
152
269
|
}
|
|
153
270
|
};
|
|
154
271
|
|
|
155
272
|
/**
|
|
156
|
-
*
|
|
273
|
+
* 提取session数据(私有方法)
|
|
157
274
|
* @param {object} ctx HTTP上下文
|
|
158
|
-
* @
|
|
159
|
-
* @param {boolean} need_refresh 是否需要刷新
|
|
275
|
+
* @returns {object} 提取的session数据
|
|
160
276
|
*/
|
|
161
|
-
Session.prototype.
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
await this._destroySession(ctx, uuid);
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
let sid = await this._saveSession(ctx, uuid);
|
|
277
|
+
Session.prototype._extractSessionData = function (ctx) {
|
|
278
|
+
const session_to_save = {};
|
|
279
|
+
|
|
280
|
+
// 使用Reflect.ownKeys确保正确枚举Proxy对象的所有属性
|
|
281
|
+
const keys = Reflect.ownKeys(ctx.session);
|
|
282
|
+
console.log(' - 保存前session对象属性列表:', keys);
|
|
172
283
|
|
|
173
|
-
|
|
174
|
-
|
|
284
|
+
for (const key of keys) {
|
|
285
|
+
// 跳过Symbol类型的属性
|
|
286
|
+
if (typeof key === 'symbol') {
|
|
287
|
+
console.log(` - 跳过Symbol属性: ${key.toString()}`);
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// 跳过函数属性和内部属性
|
|
292
|
+
if (typeof ctx.session[key] !== 'function' && !key.startsWith('_')) {
|
|
293
|
+
session_to_save[key] = ctx.session[key];
|
|
294
|
+
console.log(` - 保存属性到session_to_save: ${key} = ${ctx.session[key]}`);
|
|
295
|
+
} else {
|
|
296
|
+
console.log(` - 跳过属性: ${key} (类型: ${typeof ctx.session[key]})`);
|
|
297
|
+
}
|
|
175
298
|
}
|
|
299
|
+
|
|
300
|
+
return session_to_save;
|
|
176
301
|
};
|
|
177
302
|
|
|
178
303
|
/**
|
|
179
|
-
*
|
|
304
|
+
* 处理cookie设置(私有方法)
|
|
180
305
|
* @param {object} ctx HTTP上下文
|
|
181
|
-
* @param {string}
|
|
182
|
-
* @param {boolean} need_refresh 是否需要刷新
|
|
183
|
-
* @returns {boolean} 是否需要保存
|
|
306
|
+
* @param {string} session_id session ID
|
|
184
307
|
*/
|
|
185
|
-
Session.prototype.
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
308
|
+
Session.prototype._handleCookie = async function (ctx, session_id) {
|
|
309
|
+
if (ctx.session.uuid !== session_id) {
|
|
310
|
+
// 如果session ID改变,需要更新cookie
|
|
311
|
+
await this._store.del(session_id);
|
|
312
|
+
this._setCookie(ctx, ctx.session.uuid);
|
|
189
313
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
return true;
|
|
314
|
+
else if (ctx.session._is_new) {
|
|
315
|
+
// 新session,设置cookie
|
|
316
|
+
this._setCookie(ctx, ctx.session.uuid);
|
|
194
317
|
}
|
|
195
|
-
|
|
196
|
-
// 对于新session或修改过的session,需要保存
|
|
197
|
-
// 这里简化逻辑,总是返回true以确保session被保存
|
|
198
|
-
return true;
|
|
199
318
|
};
|
|
200
319
|
|
|
201
320
|
/**
|
|
202
|
-
*
|
|
321
|
+
* 保存session(私有方法)
|
|
203
322
|
* @param {object} ctx HTTP上下文
|
|
204
|
-
* @param {string}
|
|
205
|
-
* @returns {
|
|
323
|
+
* @param {string} session_id session ID
|
|
324
|
+
* @returns {Promise<void>} 保存结果
|
|
206
325
|
*/
|
|
207
|
-
Session.prototype.
|
|
208
|
-
|
|
326
|
+
Session.prototype._save = async function (ctx, session_id) {
|
|
327
|
+
// 如果session不存在,直接删除session
|
|
328
|
+
if (!ctx.session) {
|
|
329
|
+
await this._delSession(ctx, session_id);
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
console.log(' - save方法: 检查session._modified标志:', ctx.session._modified);
|
|
334
|
+
|
|
335
|
+
// 如果session被修改过,需要保存
|
|
336
|
+
if (ctx.session._modified) {
|
|
337
|
+
await this._handleCookie(ctx, session_id);
|
|
338
|
+
|
|
339
|
+
// 提取session数据
|
|
340
|
+
const session_to_save = this._extractSessionData(ctx);
|
|
341
|
+
|
|
342
|
+
console.log(' - 保存前的session_to_save对象:', session_to_save);
|
|
343
|
+
|
|
344
|
+
// 保存内部属性(_is_new和_modified)
|
|
345
|
+
if (ctx.session._is_new !== undefined) {
|
|
346
|
+
session_to_save._is_new = ctx.session._is_new;
|
|
347
|
+
}
|
|
348
|
+
if (ctx.session._modified !== undefined) {
|
|
349
|
+
session_to_save._modified = ctx.session._modified;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
console.log(' - 保存内部属性后的session_to_save对象:', session_to_save);
|
|
353
|
+
|
|
354
|
+
// 清理session
|
|
355
|
+
this._cleanup(ctx);
|
|
356
|
+
|
|
357
|
+
console.log(' - 清理session后的session_to_save对象:', session_to_save);
|
|
358
|
+
|
|
359
|
+
// 保存session到存储
|
|
360
|
+
return await this._store.set(ctx.session.uuid, session_to_save, this.config.max_age || 7200);
|
|
361
|
+
}
|
|
209
362
|
};
|
|
210
363
|
|
|
211
364
|
/**
|
|
212
|
-
*
|
|
365
|
+
* 保存session(公开方法)
|
|
213
366
|
* @param {object} ctx HTTP上下文
|
|
214
|
-
* @param {string}
|
|
367
|
+
* @param {string|object} session_id_or_obj session ID或session对象
|
|
368
|
+
* @returns {Promise<void>} 保存结果
|
|
215
369
|
*/
|
|
216
|
-
Session.prototype.
|
|
217
|
-
|
|
218
|
-
|
|
370
|
+
Session.prototype.save = async function (ctx, session_id_or_obj) {
|
|
371
|
+
let session_id;
|
|
372
|
+
|
|
373
|
+
if (typeof session_id_or_obj === 'string') {
|
|
374
|
+
// 如果传入的是session ID字符串
|
|
375
|
+
session_id = session_id_or_obj;
|
|
376
|
+
} else if (session_id_or_obj && typeof session_id_or_obj.getId === 'function') {
|
|
377
|
+
// 如果传入的是session对象
|
|
378
|
+
session_id = session_id_or_obj.getId();
|
|
379
|
+
} else {
|
|
380
|
+
// 如果传入的是undefined或null,尝试从ctx.session获取
|
|
381
|
+
if (ctx.session && ctx.session.getId) {
|
|
382
|
+
session_id = ctx.session.getId();
|
|
383
|
+
} else {
|
|
384
|
+
throw new TypeError('无效的session参数');
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
return await this._save(ctx, session_id);
|
|
219
389
|
};
|
|
220
390
|
|
|
221
391
|
/**
|
|
222
|
-
*
|
|
392
|
+
* 销毁session
|
|
223
393
|
* @param {object} ctx HTTP上下文
|
|
224
|
-
* @param {string}
|
|
225
|
-
* @returns {string} 新的session ID
|
|
394
|
+
* @param {string} session_id session ID
|
|
226
395
|
*/
|
|
227
|
-
Session.prototype.
|
|
228
|
-
|
|
229
|
-
|
|
396
|
+
Session.prototype._delSession = async function (ctx, session_id) {
|
|
397
|
+
ctx.cookies.set(this.config.cookie_token, null);
|
|
398
|
+
this._store.del(session_id).catch();
|
|
230
399
|
};
|
|
231
400
|
|
|
232
401
|
/**
|
|
233
402
|
* 判断是否需要设置cookie
|
|
234
|
-
* @param {string}
|
|
403
|
+
* @param {string} session_id 原session ID
|
|
235
404
|
* @param {string} sid 新session ID
|
|
236
|
-
* @param {boolean} need_refresh 是否需要刷新
|
|
237
405
|
* @returns {boolean} 是否需要设置cookie
|
|
238
406
|
*/
|
|
239
|
-
Session.prototype._shouldSetCookie = function(
|
|
240
|
-
return !
|
|
407
|
+
Session.prototype._shouldSetCookie = function (session_id, sid) {
|
|
408
|
+
return !session_id || session_id !== sid;
|
|
241
409
|
};
|
|
242
410
|
|
|
243
411
|
/**
|
|
@@ -245,15 +413,62 @@ Session.prototype._shouldSetCookie = function(uuid, sid, need_refresh) {
|
|
|
245
413
|
* @param {object} ctx HTTP上下文
|
|
246
414
|
* @param {string} sid session ID
|
|
247
415
|
*/
|
|
248
|
-
Session.prototype._setCookie = function(ctx, sid) {
|
|
249
|
-
let cg = { ...this.config.config};
|
|
416
|
+
Session.prototype._setCookie = function (ctx, sid) {
|
|
417
|
+
let cg = { ...this.config.config };
|
|
250
418
|
if (cg.max_age) {
|
|
251
419
|
cg.maxAge = cg.max_age * 1000;
|
|
252
420
|
delete cg.max_age;
|
|
253
421
|
}
|
|
254
|
-
ctx.cookies.set(this.config.
|
|
422
|
+
ctx.cookies.set(this.config.cookie_token, sid, cg);
|
|
255
423
|
};
|
|
256
424
|
|
|
257
|
-
|
|
258
|
-
|
|
425
|
+
/**
|
|
426
|
+
* 验证token时效性
|
|
427
|
+
* @param {number} end_time token过期时间戳
|
|
428
|
+
* @returns {boolean} 是否过期
|
|
429
|
+
*/
|
|
430
|
+
Session.prototype._verifyExpiration = async function (end_time) {
|
|
431
|
+
var current_time = Date.parse(new Date()) / 1000;
|
|
432
|
+
return current_time > end_time;
|
|
259
433
|
};
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* 完整的token验证流程
|
|
437
|
+
* @param {string} session_id session ID
|
|
438
|
+
* @param {string} client_ip 客户端IP
|
|
439
|
+
* @param {string} client_ua 客户端UA字符串
|
|
440
|
+
* @returns {boolean} 是否验证通过
|
|
441
|
+
*/
|
|
442
|
+
Session.prototype._checkSessionId = async function (session_id, client_ip, client_ua) {
|
|
443
|
+
try {
|
|
444
|
+
// 解码session_id
|
|
445
|
+
var data = this._helper.aesDecode(session_id,
|
|
446
|
+
this.config.encrypt_key, this._helper._getSecret(client_ua));
|
|
447
|
+
if (!data) return false;
|
|
448
|
+
let { ip, end_time } = data;
|
|
449
|
+
// 验证IP
|
|
450
|
+
if (ip !== client_ip) return false;
|
|
451
|
+
|
|
452
|
+
// 验证时效性
|
|
453
|
+
var is_expired = await this._verifyExpiration(end_time);
|
|
454
|
+
if (is_expired) {
|
|
455
|
+
if (this.config.expired_handling !== 'allow') {
|
|
456
|
+
return false;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// 所有验证通过
|
|
461
|
+
return true;
|
|
462
|
+
} catch (err) {
|
|
463
|
+
$.log.debug('Session ID验证错误:', err);
|
|
464
|
+
return false;
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
module.exports = {
|
|
469
|
+
Session,
|
|
470
|
+
session: function (options) {
|
|
471
|
+
let sess = new Session(options);
|
|
472
|
+
return sess.middleware();
|
|
473
|
+
}
|
|
474
|
+
};
|
package/lib/store.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const { Cache } = require('mm_cache');
|
|
2
2
|
|
|
3
3
|
if (!$.cache) {
|
|
4
|
-
$.cache = new
|
|
4
|
+
$.cache = new Cache();
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -21,34 +21,17 @@ class Store {
|
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
/**
|
|
25
|
-
* 获取session ID
|
|
26
|
-
* @param {object} ctx HTTP上下文
|
|
27
|
-
* @returns {string} session ID
|
|
28
|
-
*/
|
|
29
|
-
Store.prototype.getID = async function(ctx) {
|
|
30
|
-
var header = ctx.request.header;
|
|
31
|
-
var user_agent = header['user-agent'];
|
|
32
|
-
if (!user_agent) {
|
|
33
|
-
user_agent = 'mm';
|
|
34
|
-
}
|
|
35
|
-
var start_hash = user_agent.md5().substring(0, 32);
|
|
36
|
-
var time_stamp = Date.parse(new Date()) / 1000;
|
|
37
|
-
var uuid = (ctx.ip + '_' + time_stamp).aes_encode(start_hash);
|
|
38
|
-
return uuid;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
24
|
/**
|
|
42
25
|
* 获取session缓存
|
|
43
26
|
* @param {string} uuid 客户端唯一ID
|
|
44
27
|
* @returns {object|null} session对象或null
|
|
45
28
|
*/
|
|
46
|
-
Store.prototype.get = async function(uuid) {
|
|
29
|
+
Store.prototype.get = async function (uuid) {
|
|
47
30
|
if (!$.cache.has(this.key + uuid)) return undefined;
|
|
48
31
|
// 我们正在解码数据来自我们的srote, 我们假定它是在储存之前
|
|
49
32
|
var val = await $.cache.get(this.key + uuid);
|
|
50
33
|
if (val) {
|
|
51
|
-
if (typeof(val) == 'string') {
|
|
34
|
+
if (typeof (val) == 'string') {
|
|
52
35
|
return JSON.parse(val);
|
|
53
36
|
}
|
|
54
37
|
return val;
|
|
@@ -59,44 +42,32 @@ Store.prototype.get = async function(uuid) {
|
|
|
59
42
|
|
|
60
43
|
/**
|
|
61
44
|
* 设置session到缓存
|
|
45
|
+
* @param {string} uuid session ID
|
|
62
46
|
* @param {object} session session对象
|
|
63
|
-
* @param {
|
|
64
|
-
* @param {string} options.uuid 客户端唯一表示ID
|
|
65
|
-
* @param {number} options.max_age 最大寿命
|
|
66
|
-
* @param {object} ctx HTTP请求上下文
|
|
47
|
+
* @param {number} max_age 最大寿命(秒)
|
|
67
48
|
* @returns {string} session ID
|
|
68
49
|
*/
|
|
69
|
-
Store.prototype.set = async function(session, {
|
|
70
|
-
uuid,
|
|
71
|
-
max_age
|
|
72
|
-
} = {}, ctx) {
|
|
73
|
-
let session_uuid = uuid;
|
|
74
|
-
let session_max_age = max_age;
|
|
75
|
-
|
|
76
|
-
if (!session_uuid) {
|
|
77
|
-
session_uuid = await this.getID(ctx);
|
|
78
|
-
}
|
|
79
|
-
if (!session_max_age) {
|
|
80
|
-
session_max_age = 7200;
|
|
81
|
-
}
|
|
82
|
-
|
|
50
|
+
Store.prototype.set = async function (uuid, session, max_age = 7200) {
|
|
83
51
|
let session_str = session;
|
|
84
52
|
try {
|
|
85
|
-
|
|
53
|
+
console.log(' - Store.set接收到的session对象:', session);
|
|
54
|
+
if (typeof (session_str) == 'object') {
|
|
86
55
|
session_str = JSON.stringify(session_str);
|
|
56
|
+
console.log(' - JSON序列化后的session:', session_str);
|
|
87
57
|
}
|
|
88
|
-
|
|
58
|
+
|
|
59
|
+
await $.cache.set(this.key + uuid, session_str, max_age);
|
|
89
60
|
} catch (err) {
|
|
90
61
|
$.log.debug('Set session error:', err);
|
|
91
62
|
}
|
|
92
|
-
return
|
|
63
|
+
return uuid;
|
|
93
64
|
};
|
|
94
65
|
|
|
95
66
|
/**
|
|
96
|
-
*
|
|
67
|
+
* 删除缓存
|
|
97
68
|
* @param {string} uuid 客户端唯一ID
|
|
98
69
|
*/
|
|
99
|
-
Store.prototype.
|
|
70
|
+
Store.prototype.del = function (uuid) {
|
|
100
71
|
$.cache.del(this.key + uuid);
|
|
101
72
|
};
|
|
102
73
|
|