mm_session 1.5.9 → 1.6.1
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/lib/helper.js +15 -19
- package/lib/session.js +37 -65
- package/lib/store.js +3 -3
- package/package.json +3 -3
package/lib/helper.js
CHANGED
|
@@ -47,28 +47,25 @@ Helper.prototype.aesDecode = function (text, key, iv) {
|
|
|
47
47
|
|
|
48
48
|
|
|
49
49
|
/**
|
|
50
|
-
*
|
|
51
|
-
* @param {string}
|
|
52
|
-
* @returns {string}
|
|
50
|
+
* 从key派生IV(固定派生,不依赖user_agent)
|
|
51
|
+
* @param {string} key 密钥
|
|
52
|
+
* @returns {string} IV(16字节)
|
|
53
53
|
*/
|
|
54
|
-
Helper.prototype.
|
|
55
|
-
|
|
56
|
-
return user_agent.md5().substring(0, 16);
|
|
54
|
+
Helper.prototype._getIvFromKey = function (key) {
|
|
55
|
+
return key.md5().substring(0, 16);
|
|
57
56
|
};
|
|
58
57
|
|
|
59
58
|
/**
|
|
60
59
|
* 生成session ID
|
|
61
60
|
* @param {string} key 密钥
|
|
62
61
|
* @param {string} ip 客户端IP
|
|
63
|
-
* @param {string} user_agent
|
|
62
|
+
* @param {string} user_agent 用户代理字符串(不再用于IV,仅保留参数兼容)
|
|
64
63
|
* @param {number} uuid_expire 过期时间(秒)
|
|
65
64
|
* @returns {string} session ID
|
|
66
65
|
*/
|
|
67
66
|
Helper.prototype.genId = function (key, ip, user_agent = 'mm', uuid_expire = 604800) {
|
|
68
|
-
|
|
69
|
-
var iv = this._getSecret(user_agent);
|
|
67
|
+
var iv = this._getIvFromKey(key);
|
|
70
68
|
var end_time = new Date().addSeconds(uuid_expire).stamp();
|
|
71
|
-
// 加密
|
|
72
69
|
var uuid = this.aesEncode(ip + '_' + end_time, key, iv);
|
|
73
70
|
return uuid;
|
|
74
71
|
};
|
|
@@ -77,20 +74,19 @@ Helper.prototype.genId = function (key, ip, user_agent = 'mm', uuid_expire = 604
|
|
|
77
74
|
* 解析session ID
|
|
78
75
|
* @param {string} key 密钥
|
|
79
76
|
* @param {string} uuid session ID
|
|
80
|
-
* @param {string} user_agent
|
|
77
|
+
* @param {string} user_agent 用户代理字符串(不再用于IV,仅保留参数兼容)
|
|
81
78
|
* @returns {object} 解析结果
|
|
82
79
|
*/
|
|
83
80
|
Helper.prototype.parseId = function (key, uuid, user_agent = 'mm') {
|
|
84
|
-
|
|
85
|
-
var iv = this._getSecret(user_agent);
|
|
86
|
-
// 解密
|
|
81
|
+
var iv = this._getIvFromKey(key);
|
|
87
82
|
var dec = this.aesDecode(uuid, key, iv);
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
83
|
+
var parts = dec.split('_');
|
|
84
|
+
if (parts.length !== 2) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
91
87
|
return {
|
|
92
|
-
ip,
|
|
93
|
-
end_time
|
|
88
|
+
ip: parts[0],
|
|
89
|
+
end_time: Number(parts[1])
|
|
94
90
|
};
|
|
95
91
|
};
|
|
96
92
|
|
package/lib/session.js
CHANGED
|
@@ -18,20 +18,14 @@ class Session {
|
|
|
18
18
|
* 默认配置
|
|
19
19
|
*/
|
|
20
20
|
static config = {
|
|
21
|
-
// 密钥
|
|
22
21
|
encrypt_key: $.config?.encrypt_key ? $.config.encrypt_key : 'mm'.md5().substring(0, 16),
|
|
23
|
-
// session存储在缓存中的key前缀,默认'mm:uuid'
|
|
24
22
|
key_prefix: $.dict.session_id || 'mm:uuid',
|
|
25
|
-
// session ID存储在cookie中的字段名,默认'mm:uuid'
|
|
26
23
|
cookie_token: $.dict.cookie_token || 'mm:uuid',
|
|
27
|
-
// HTTP请求头中用于传递session ID的字段名,默认'x-auth-token'
|
|
28
24
|
header_token: $.dict.header_token || 'x-auth-token',
|
|
29
|
-
// session ID过期时间,单位秒,默认7天
|
|
30
25
|
uuid_expire: 60 * 60 * 24 * 7,
|
|
31
|
-
// session过期时间,单位秒,默认2小时
|
|
32
26
|
max_age: 60 * 60 * 2,
|
|
33
|
-
// 过期token处理选项:'reject'(拒绝访问)或'allow'(允许访问缓存)
|
|
34
27
|
expired_handling: 'reject',
|
|
28
|
+
options: { path: '/' },
|
|
35
29
|
};
|
|
36
30
|
|
|
37
31
|
/**
|
|
@@ -89,30 +83,23 @@ Session.prototype.middleware = function () {
|
|
|
89
83
|
* @returns {object} session对象
|
|
90
84
|
*/
|
|
91
85
|
Session.prototype.get = async function (ctx) {
|
|
92
|
-
// 获取协议头信息
|
|
93
86
|
let { ip, user_agent } = this._getHeaderInfo(ctx);
|
|
94
|
-
// 获取session ID
|
|
95
87
|
let session_id = this.getId(ctx);
|
|
96
88
|
|
|
97
|
-
// 验证session ID标识的有效性
|
|
98
89
|
if (session_id) {
|
|
99
90
|
let is_valid = await this._checkSessionId(session_id, ip, user_agent);
|
|
100
91
|
if (!is_valid) {
|
|
101
|
-
// token验证失败,返回null创建新session
|
|
102
92
|
session_id = null;
|
|
103
93
|
}
|
|
104
94
|
}
|
|
105
95
|
|
|
106
|
-
// 获取session对象
|
|
107
96
|
let session = await this._get(session_id);
|
|
108
97
|
|
|
109
98
|
if (!session.uuid) {
|
|
110
99
|
session.uuid = this._genId(ip, user_agent);
|
|
111
100
|
}
|
|
112
|
-
|
|
101
|
+
this._bindSessionMethods(session, ctx);
|
|
113
102
|
ctx.session = this._create(session);
|
|
114
|
-
// 追加方法
|
|
115
|
-
this._addMethod(ctx);
|
|
116
103
|
return ctx.session;
|
|
117
104
|
};
|
|
118
105
|
|
|
@@ -122,9 +109,13 @@ Session.prototype.get = async function (ctx) {
|
|
|
122
109
|
* @param {Function} next 下一个中间件
|
|
123
110
|
*/
|
|
124
111
|
Session.prototype._handle = async function (ctx, next) {
|
|
125
|
-
let session_id =
|
|
112
|
+
let session_id = this.getId(ctx);
|
|
113
|
+
await this.get(ctx);
|
|
126
114
|
await next();
|
|
127
115
|
await this._save(ctx, session_id);
|
|
116
|
+
if (ctx.session) {
|
|
117
|
+
this._setCookie(ctx, ctx.session.uuid);
|
|
118
|
+
}
|
|
128
119
|
};
|
|
129
120
|
|
|
130
121
|
/**
|
|
@@ -133,24 +124,21 @@ Session.prototype._handle = async function (ctx, next) {
|
|
|
133
124
|
* @returns {string} 客户端IP地址
|
|
134
125
|
*/
|
|
135
126
|
Session.prototype._getClientIP = function (ctx) {
|
|
136
|
-
|
|
127
|
+
var ip;
|
|
137
128
|
if (ctx.ip) {
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
129
|
+
ip = ctx.ip;
|
|
130
|
+
} else if (ctx.headers['x-forwarded-for']) {
|
|
131
|
+
ip = ctx.headers['x-forwarded-for'].split(',')[0].trim();
|
|
132
|
+
} else if (ctx.headers['x-real-ip']) {
|
|
133
|
+
ip = ctx.headers['x-real-ip'];
|
|
134
|
+
} else {
|
|
135
|
+
ip = '127.0.0.1';
|
|
145
136
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
return ctx.headers['x-real-ip'];
|
|
137
|
+
// 归一化本地回环地址,避免 IPv6/IPv4 格式差异导致校验失败
|
|
138
|
+
if (ip === '::1' || ip === '::ffff:127.0.0.1' || ip === 'localhost') {
|
|
139
|
+
ip = '127.0.0.1';
|
|
150
140
|
}
|
|
151
|
-
|
|
152
|
-
// 默认返回本地IP
|
|
153
|
-
return '127.0.0.1';
|
|
141
|
+
return ip;
|
|
154
142
|
};
|
|
155
143
|
|
|
156
144
|
/**
|
|
@@ -255,27 +243,29 @@ Session.prototype._create = function (session) {
|
|
|
255
243
|
};
|
|
256
244
|
|
|
257
245
|
/**
|
|
258
|
-
*
|
|
246
|
+
* 给session目标对象绑定方法(在创建Proxy前调用,避免触发set陷阱)
|
|
247
|
+
* @param {object} session session目标对象
|
|
259
248
|
* @param {object} ctx HTTP上下文
|
|
260
249
|
*/
|
|
261
|
-
Session.prototype.
|
|
250
|
+
Session.prototype._bindSessionMethods = function (session, ctx) {
|
|
251
|
+
var self = this;
|
|
252
|
+
|
|
262
253
|
/**
|
|
263
254
|
* 刷新session
|
|
264
255
|
*/
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
ctx.session._modified = true;
|
|
256
|
+
session.refresh = function () {
|
|
257
|
+
this._modified = true;
|
|
268
258
|
};
|
|
269
259
|
|
|
270
260
|
/**
|
|
271
261
|
* 获取session ID
|
|
272
262
|
* @returns {string} session ID
|
|
273
263
|
*/
|
|
274
|
-
|
|
275
|
-
if (!
|
|
276
|
-
|
|
264
|
+
session.getId = function () {
|
|
265
|
+
if (!this.uuid) {
|
|
266
|
+
this.uuid = self.genId(ctx);
|
|
277
267
|
}
|
|
278
|
-
return
|
|
268
|
+
return this.uuid;
|
|
279
269
|
};
|
|
280
270
|
};
|
|
281
271
|
|
|
@@ -340,31 +330,18 @@ Session.prototype._handleCookie = async function (ctx, session_id) {
|
|
|
340
330
|
* @returns {Promise<void>} 保存结果
|
|
341
331
|
*/
|
|
342
332
|
Session.prototype._save = async function (ctx, session_id) {
|
|
343
|
-
// 如果session不存在,直接删除session
|
|
344
333
|
if (!ctx.session) {
|
|
345
334
|
await this._delSession(ctx, session_id);
|
|
346
335
|
return;
|
|
347
336
|
}
|
|
348
337
|
|
|
349
|
-
|
|
350
|
-
if (ctx.session._modified) {
|
|
338
|
+
if (ctx.session._is_new || ctx.session._modified) {
|
|
351
339
|
await this._handleCookie(ctx, session_id);
|
|
352
340
|
|
|
353
|
-
// 提取session数据
|
|
354
341
|
const session_to_save = this._extractSessionData(ctx);
|
|
355
342
|
|
|
356
|
-
// 保存内部属性(_is_new和_modified)
|
|
357
|
-
if (ctx.session._is_new !== undefined) {
|
|
358
|
-
session_to_save._is_new = ctx.session._is_new;
|
|
359
|
-
}
|
|
360
|
-
if (ctx.session._modified !== undefined) {
|
|
361
|
-
session_to_save._modified = ctx.session._modified;
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
// 清理session
|
|
365
343
|
this._cleanup(ctx);
|
|
366
344
|
|
|
367
|
-
// 保存session到存储
|
|
368
345
|
return await this._store.set(ctx.session.uuid, session_to_save, this.config.max_age || 7200);
|
|
369
346
|
}
|
|
370
347
|
};
|
|
@@ -403,7 +380,7 @@ Session.prototype.save = async function (ctx, session_id_or_obj) {
|
|
|
403
380
|
*/
|
|
404
381
|
Session.prototype._delSession = async function (ctx, session_id) {
|
|
405
382
|
ctx.cookies.set(this.config.cookie_token, null);
|
|
406
|
-
this._store.del(session_id)
|
|
383
|
+
await this._store.del(session_id);
|
|
407
384
|
};
|
|
408
385
|
|
|
409
386
|
/**
|
|
@@ -422,10 +399,9 @@ Session.prototype._shouldSetCookie = function (session_id, sid) {
|
|
|
422
399
|
* @param {string} sid session ID
|
|
423
400
|
*/
|
|
424
401
|
Session.prototype._setCookie = function (ctx, sid) {
|
|
425
|
-
let cg = { ...this.config.
|
|
426
|
-
if (cg.
|
|
427
|
-
cg.maxAge =
|
|
428
|
-
delete cg.max_age;
|
|
402
|
+
let cg = { ...this.config.options };
|
|
403
|
+
if (this.config.max_age && !cg.maxAge) {
|
|
404
|
+
cg.maxAge = this.config.max_age * 1000;
|
|
429
405
|
}
|
|
430
406
|
ctx.cookies.set(this.config.cookie_token, sid, cg);
|
|
431
407
|
};
|
|
@@ -435,7 +411,7 @@ Session.prototype._setCookie = function (ctx, sid) {
|
|
|
435
411
|
* @param {number} end_time token过期时间戳
|
|
436
412
|
* @returns {boolean} 是否过期
|
|
437
413
|
*/
|
|
438
|
-
Session.prototype._verifyExpiration =
|
|
414
|
+
Session.prototype._verifyExpiration = function (end_time) {
|
|
439
415
|
var current_time = Date.parse(new Date()) / 1000;
|
|
440
416
|
return current_time > end_time;
|
|
441
417
|
};
|
|
@@ -449,22 +425,18 @@ Session.prototype._verifyExpiration = async function (end_time) {
|
|
|
449
425
|
*/
|
|
450
426
|
Session.prototype._checkSessionId = async function (session_id, client_ip, client_ua) {
|
|
451
427
|
try {
|
|
452
|
-
// 解码session_id
|
|
453
428
|
var data = this._helper.parseId(this.config.encrypt_key, session_id, client_ua);
|
|
454
429
|
if (!data) return false;
|
|
455
430
|
let { ip, end_time } = data;
|
|
456
|
-
// 验证IP
|
|
457
431
|
if (ip !== client_ip) return false;
|
|
458
432
|
|
|
459
|
-
|
|
460
|
-
var is_expired = await this._verifyExpiration(end_time);
|
|
433
|
+
var is_expired = this._verifyExpiration(end_time);
|
|
461
434
|
if (is_expired) {
|
|
462
435
|
if (this.config.expired_handling !== 'allow') {
|
|
463
436
|
return false;
|
|
464
437
|
}
|
|
465
438
|
}
|
|
466
439
|
|
|
467
|
-
// 所有验证通过
|
|
468
440
|
return true;
|
|
469
441
|
} catch (err) {
|
|
470
442
|
$.log.debug('Session ID验证错误:', err);
|
package/lib/store.js
CHANGED
|
@@ -27,7 +27,7 @@ class Store {
|
|
|
27
27
|
* @returns {object|null} session对象或null
|
|
28
28
|
*/
|
|
29
29
|
Store.prototype.get = async function (uuid) {
|
|
30
|
-
if (!$.cache.has(this.key + uuid)) return
|
|
30
|
+
if (!$.cache.has(this.key + uuid)) return null;
|
|
31
31
|
// 我们正在解码数据来自我们的srote, 我们假定它是在储存之前
|
|
32
32
|
var val = await $.cache.get(this.key + uuid);
|
|
33
33
|
if (val) {
|
|
@@ -65,8 +65,8 @@ Store.prototype.set = async function (uuid, session, max_age = 7200) {
|
|
|
65
65
|
* 删除缓存
|
|
66
66
|
* @param {string} uuid 客户端唯一ID
|
|
67
67
|
*/
|
|
68
|
-
Store.prototype.del = function (uuid) {
|
|
69
|
-
$.cache.del(this.key + uuid);
|
|
68
|
+
Store.prototype.del = async function (uuid) {
|
|
69
|
+
await $.cache.del(this.key + uuid);
|
|
70
70
|
};
|
|
71
71
|
|
|
72
72
|
module.exports = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mm_session",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.1",
|
|
4
4
|
"description": "这是超级美眉session函数模块,用于web服务端session缓存",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -30,10 +30,10 @@
|
|
|
30
30
|
},
|
|
31
31
|
"homepage": "https://gitee.com/qiuwenwu91/mm_session#readme",
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"mm_cache": "^1.
|
|
33
|
+
"mm_cache": "^1.5.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"eslint-plugin-jsdoc": "^63.0.
|
|
36
|
+
"eslint-plugin-jsdoc": "^63.0.10",
|
|
37
37
|
"mm_eslint": "^1.7.5"
|
|
38
38
|
}
|
|
39
39
|
}
|