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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mm_session",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.3",
|
|
4
4
|
"description": "这是超级美眉session函数模块,用于web服务端session缓存",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -29,11 +29,11 @@
|
|
|
29
29
|
"url": "https://gitee.com/qiuwenwu91/mm_session/issues"
|
|
30
30
|
},
|
|
31
31
|
"homepage": "https://gitee.com/qiuwenwu91/mm_session#readme",
|
|
32
|
-
"dependencies": {
|
|
33
|
-
"mm_cachebase": "^1.9.2"
|
|
34
|
-
},
|
|
35
32
|
"devDependencies": {
|
|
36
33
|
"eslint-plugin-jsdoc": "^61.5.0",
|
|
37
|
-
"mm_eslint": "^1.0.
|
|
34
|
+
"mm_eslint": "^1.0.8"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"mm_cache": "^1.4.7"
|
|
38
38
|
}
|
|
39
39
|
}
|
package/test.js
CHANGED
|
@@ -1,190 +1,328 @@
|
|
|
1
|
-
|
|
1
|
+
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
// 简单的AES编码模拟
|
|
5
|
-
return Buffer.from(this).toString('base64') + '_' + key.substring(0, 8);
|
|
6
|
-
};
|
|
3
|
+
const { session, Session, Store } = require('./index.js');
|
|
7
4
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
use(middleware) {
|
|
15
|
-
this.middlewares.push(middleware);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
async handleRequest(ctx) {
|
|
19
|
-
// 执行中间件链
|
|
20
|
-
const executeMiddleware = async (index) => {
|
|
21
|
-
if (index < this.middlewares.length) {
|
|
22
|
-
const middleware = this.middlewares[index];
|
|
23
|
-
await middleware(ctx, () => executeMiddleware(index + 1));
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
await executeMiddleware(0);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// 模拟Koa的上下文对象
|
|
5
|
+
/**
|
|
6
|
+
* 模拟Koa上下文对象
|
|
7
|
+
* @class MockContext
|
|
8
|
+
* @param {object} headers HTTP请求头
|
|
9
|
+
* @param {object} cookies cookie数据
|
|
10
|
+
*/
|
|
32
11
|
class MockContext {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
12
|
+
/**
|
|
13
|
+
* 构造函数
|
|
14
|
+
* @param {object} headers HTTP请求头
|
|
15
|
+
* @param {object} cookies cookie数据
|
|
16
|
+
*/
|
|
17
|
+
constructor(headers = {}, cookies = {}) {
|
|
18
|
+
this.headers = headers;
|
|
19
|
+
this._cookies = cookies;
|
|
36
20
|
this.ip = '127.0.0.1';
|
|
37
|
-
this.
|
|
38
|
-
|
|
39
|
-
|
|
21
|
+
this.body = null;
|
|
22
|
+
this.status = null;
|
|
23
|
+
|
|
24
|
+
// 模拟cookies对象
|
|
25
|
+
this.cookies = {
|
|
26
|
+
get: (name) => {
|
|
27
|
+
return this._cookies[name] || null;
|
|
28
|
+
},
|
|
29
|
+
set: (name, value, options = {}) => {
|
|
30
|
+
this._cookies[name] = value;
|
|
40
31
|
}
|
|
41
32
|
};
|
|
42
|
-
this.session = null;
|
|
43
33
|
}
|
|
44
34
|
}
|
|
45
35
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
36
|
+
/**
|
|
37
|
+
* 模拟Koa next函数
|
|
38
|
+
* @returns {Promise<void>} 空Promise
|
|
39
|
+
*/
|
|
40
|
+
const mock_next = () => Promise.resolve();
|
|
51
41
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
42
|
+
/**
|
|
43
|
+
* 测试基本session中间件功能
|
|
44
|
+
* @returns {Promise<void>} 测试结果
|
|
45
|
+
*/
|
|
46
|
+
async function testBasicSession() {
|
|
47
|
+
console.log('1. 测试基本session中间件功能');
|
|
48
|
+
try {
|
|
49
|
+
const session_mw = session();
|
|
50
|
+
const ctx = new MockContext();
|
|
51
|
+
|
|
52
|
+
await session_mw(ctx, mock_next);
|
|
55
53
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
54
|
+
if (ctx.session) {
|
|
55
|
+
console.log('✓ session对象创建成功');
|
|
56
|
+
console.log('✓ session ID:', ctx.session.getId());
|
|
59
57
|
} else {
|
|
60
|
-
|
|
58
|
+
console.log('✗ session对象创建失败');
|
|
61
59
|
}
|
|
60
|
+
} catch (err) {
|
|
61
|
+
console.log('✗ 测试失败:', err.message);
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
/**
|
|
66
|
+
* 测试session数据读写功能
|
|
67
|
+
* @returns {Promise<void>} 测试结果
|
|
68
|
+
*/
|
|
69
|
+
async function testSessionDataRW() {
|
|
70
|
+
console.log('\n2. 测试session数据读写功能');
|
|
69
71
|
try {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
const session_mw = session();
|
|
73
|
+
const ctx = new MockContext();
|
|
74
|
+
|
|
75
|
+
await session_mw(ctx, async () => {
|
|
76
|
+
// 在中间件中设置session数据
|
|
77
|
+
ctx.session.user_id = 123;
|
|
78
|
+
ctx.session.username = 'testuser';
|
|
79
|
+
ctx.session.is_logged_in = true;
|
|
75
80
|
});
|
|
76
|
-
console.log('✅ Session实例创建成功\n');
|
|
77
81
|
|
|
78
|
-
|
|
79
|
-
console.log('
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
82
|
+
console.log('✓ session数据设置成功');
|
|
83
|
+
console.log(' - user_id:', ctx.session.user_id);
|
|
84
|
+
console.log(' - username:', ctx.session.username);
|
|
85
|
+
console.log(' - is_logged_in:', ctx.session.is_logged_in);
|
|
86
|
+
} catch (err) {
|
|
87
|
+
console.log('✗ 测试失败:', err.message);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* 创建测试session
|
|
93
|
+
* @param {Function} session_mw session中间件
|
|
94
|
+
* @returns {Promise<object>} 包含session ID和上下文的对象
|
|
95
|
+
*/
|
|
96
|
+
async function createTestSession(session_mw) {
|
|
97
|
+
const ctx1 = new MockContext();
|
|
98
|
+
|
|
99
|
+
// 使用标准的中间件调用方式,确保保存逻辑被执行
|
|
100
|
+
await session_mw(ctx1, async () => {
|
|
101
|
+
// 在next()函数中设置session数据
|
|
102
|
+
ctx1.session.test_data = 'persistent_data';
|
|
103
|
+
// 强制标记session为已修改状态
|
|
104
|
+
ctx1.session._modified = true;
|
|
105
|
+
|
|
106
|
+
// 调试:检查session对象在设置数据后的状态
|
|
107
|
+
console.log(' - 第一次请求session对象状态:', {
|
|
108
|
+
uuid: ctx1.session.uuid,
|
|
109
|
+
test_data: ctx1.session.test_data,
|
|
110
|
+
_modified: ctx1.session._modified,
|
|
111
|
+
_is_new: ctx1.session._is_new
|
|
94
112
|
});
|
|
95
113
|
|
|
96
|
-
|
|
114
|
+
// 调试:检查session是否被标记为已修改
|
|
115
|
+
console.log(' - session._modified标志:', ctx1.session._modified);
|
|
116
|
+
});
|
|
97
117
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
118
|
+
// 调试:检查第一次请求后session是否被正确保存
|
|
119
|
+
console.log(' - 第一次请求后检查session._modified标志:', ctx1.session._modified);
|
|
120
|
+
|
|
121
|
+
const session_id = ctx1.session.getId();
|
|
122
|
+
console.log('✓ 第一次请求session ID:', session_id);
|
|
123
|
+
|
|
124
|
+
return { ctx1, session_id };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* 验证session恢复结果
|
|
129
|
+
* @param {object} ctx2 第二次请求的上下文
|
|
130
|
+
* @param {string} session_id session ID
|
|
131
|
+
* @returns {Promise<boolean>} 恢复是否成功
|
|
132
|
+
*/
|
|
133
|
+
async function verifySessionRecover(ctx2, session_id) {
|
|
134
|
+
if (ctx2.session && ctx2.session.test_data === 'persistent_data') {
|
|
135
|
+
console.log('✓ session数据恢复成功');
|
|
136
|
+
return true;
|
|
137
|
+
} else {
|
|
138
|
+
console.log('✗ session数据恢复失败');
|
|
139
|
+
console.log(' - ctx2.session:', ctx2.session);
|
|
140
|
+
if (ctx2.session) {
|
|
141
|
+
console.log(' - test_data:', ctx2.session.test_data);
|
|
142
|
+
console.log(' - session ID是否匹配:', ctx2.session.uuid === session_id);
|
|
143
|
+
|
|
144
|
+
// 调试:直接检查存储中是否有数据
|
|
145
|
+
const store = new Store();
|
|
146
|
+
const stored_data = await store.get(session_id);
|
|
147
|
+
console.log(' - 存储中的数据:', stored_data);
|
|
148
|
+
|
|
149
|
+
// 调试:检查第一次请求后存储中的数据
|
|
150
|
+
const stored_data_first = await store.get(session_id);
|
|
151
|
+
console.log(' - 第一次请求后存储中的数据:', stored_data_first);
|
|
114
152
|
}
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
115
156
|
|
|
116
|
-
|
|
117
|
-
|
|
157
|
+
/**
|
|
158
|
+
* 测试从cookie中恢复session
|
|
159
|
+
* @returns {Promise<void>} 测试结果
|
|
160
|
+
*/
|
|
161
|
+
async function testSessionRecovery() {
|
|
162
|
+
console.log('\n3. 测试从cookie中恢复session');
|
|
163
|
+
try {
|
|
164
|
+
const session_mw = session();
|
|
118
165
|
|
|
119
|
-
//
|
|
120
|
-
const
|
|
121
|
-
if (sessionCookie) {
|
|
122
|
-
console.log('✅ session保存成功,cookie已设置');
|
|
123
|
-
console.log(' session ID:', sessionCookie);
|
|
124
|
-
} else {
|
|
125
|
-
console.log('⚠️ session保存但cookie未设置');
|
|
126
|
-
}
|
|
166
|
+
// 创建测试session
|
|
167
|
+
const { session_id } = await createTestSession(session_mw);
|
|
127
168
|
|
|
128
|
-
//
|
|
129
|
-
console.log('\n测试5: 测试session读取');
|
|
169
|
+
// 第二次请求使用相同的session ID(通过cookie传递)
|
|
130
170
|
const ctx2 = new MockContext();
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
171
|
+
ctx2.cookies['mm:uuid'] = session_id;
|
|
172
|
+
|
|
173
|
+
await session_mw(ctx2, mock_next);
|
|
174
|
+
|
|
175
|
+
// 验证恢复结果
|
|
176
|
+
await verifySessionRecover(ctx2, session_id);
|
|
177
|
+
} catch (err) {
|
|
178
|
+
console.log('✗ 测试失败:', err.message);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* 测试session刷新功能
|
|
184
|
+
* @returns {Promise<void>} 测试结果
|
|
185
|
+
*/
|
|
186
|
+
async function testSessionRefresh() {
|
|
187
|
+
console.log('\n4. 测试session刷新功能');
|
|
188
|
+
try {
|
|
189
|
+
const session_mw = session();
|
|
190
|
+
const ctx = new MockContext();
|
|
191
|
+
|
|
192
|
+
await session_mw(ctx, async () => {
|
|
193
|
+
ctx.session.original_data = 'original';
|
|
194
|
+
ctx.session.refresh(); // 刷新session
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
console.log('✓ session刷新功能正常');
|
|
198
|
+
} catch (err) {
|
|
199
|
+
console.log('✗ 测试失败:', err.message);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* 测试自定义配置
|
|
205
|
+
* @returns {Promise<void>} 测试结果
|
|
206
|
+
*/
|
|
207
|
+
async function testCustomConfig() {
|
|
208
|
+
console.log('\n5. 测试自定义配置');
|
|
209
|
+
try {
|
|
210
|
+
const custom_cfg = {
|
|
211
|
+
key_prefix: 'custom:session',
|
|
212
|
+
cookie_token: 'custom_token',
|
|
213
|
+
max_age: 3600 // 1小时
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const session_mw = session(custom_cfg);
|
|
217
|
+
const ctx = new MockContext();
|
|
218
|
+
|
|
219
|
+
await session_mw(ctx, mock_next);
|
|
220
|
+
|
|
221
|
+
console.log('✓ 自定义配置生效');
|
|
222
|
+
console.log(' - session ID长度:', ctx.session.getId().length);
|
|
223
|
+
} catch (err) {
|
|
224
|
+
console.log('✗ 测试失败:', err.message);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* 测试Session类直接使用
|
|
230
|
+
* @returns {Promise<void>} 测试结果
|
|
231
|
+
*/
|
|
232
|
+
async function testSessionClass() {
|
|
233
|
+
console.log('\n6. 测试Session类直接使用');
|
|
234
|
+
try {
|
|
235
|
+
const sess = new Session();
|
|
236
|
+
const ctx = new MockContext();
|
|
237
|
+
|
|
238
|
+
const session_obj = await sess.get(ctx);
|
|
239
|
+
console.log('✓ Session类get方法正常');
|
|
240
|
+
console.log(' - 获取的session对象:', session_obj);
|
|
241
|
+
|
|
242
|
+
// 测试保存session
|
|
243
|
+
await sess.save(ctx, session_obj);
|
|
244
|
+
console.log('✓ Session类save方法正常');
|
|
245
|
+
|
|
246
|
+
// 测试生成session ID
|
|
247
|
+
const new_sess_id = sess.genId(ctx);
|
|
248
|
+
console.log('✓ Session类genId方法正常');
|
|
249
|
+
console.log(' - 生成的session ID:', new_sess_id);
|
|
250
|
+
} catch (err) {
|
|
251
|
+
console.log('✗ 测试失败:', err.message);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* 测试Store类
|
|
257
|
+
* @returns {Promise<void>} 测试结果
|
|
258
|
+
*/
|
|
259
|
+
async function testStoreClass() {
|
|
260
|
+
console.log('\n7. 测试Store类');
|
|
261
|
+
try {
|
|
262
|
+
const store = new Store('test_prefix');
|
|
263
|
+
const test_data = { key: 'value', number: 42 };
|
|
264
|
+
|
|
265
|
+
// 测试设置数据
|
|
266
|
+
await store.set('test_key', test_data, 60);
|
|
267
|
+
console.log('✓ Store类set方法正常');
|
|
268
|
+
|
|
269
|
+
// 测试获取数据
|
|
270
|
+
const ret_data = await store.get('test_key');
|
|
271
|
+
if (ret_data && ret_data.key === 'value') {
|
|
272
|
+
console.log('✓ Store类get方法正常');
|
|
273
|
+
console.log(' - 获取的数据:', ret_data);
|
|
144
274
|
} else {
|
|
145
|
-
console.log('
|
|
275
|
+
console.log('✗ Store类get方法异常');
|
|
146
276
|
}
|
|
147
277
|
|
|
148
|
-
//
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
console.log(' 生成的session ID:', sessionId);
|
|
156
|
-
|
|
157
|
-
// 测试7: 测试session销毁
|
|
158
|
-
console.log('\n测试7: 测试session销毁');
|
|
159
|
-
const ctx3 = new MockContext();
|
|
160
|
-
|
|
161
|
-
// 添加一个中间件来测试session销毁
|
|
162
|
-
const destroyApp = new MockKoaApp();
|
|
163
|
-
destroyApp.use(session.middleware());
|
|
164
|
-
destroyApp.use(async (ctx, next) => {
|
|
165
|
-
// 设置session为null触发销毁
|
|
166
|
-
ctx.session = null;
|
|
167
|
-
await next();
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
await destroyApp.handleRequest(ctx3);
|
|
171
|
-
console.log('✅ session销毁逻辑测试完成');
|
|
278
|
+
// 测试删除数据
|
|
279
|
+
store.del('test_key');
|
|
280
|
+
console.log('✓ Store类del方法正常');
|
|
281
|
+
} catch (err) {
|
|
282
|
+
console.log('✗ 测试失败:', err.message);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
172
285
|
|
|
173
|
-
|
|
286
|
+
/**
|
|
287
|
+
* 测试session中间件
|
|
288
|
+
*/
|
|
289
|
+
async function testSession() {
|
|
290
|
+
console.log('=== 开始测试session中间件 ===\n');
|
|
174
291
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
292
|
+
await testBasicSession();
|
|
293
|
+
await testSessionDataRW();
|
|
294
|
+
await testSessionRecovery();
|
|
295
|
+
await testSessionRefresh();
|
|
296
|
+
await testCustomConfig();
|
|
297
|
+
await testSessionClass();
|
|
298
|
+
await testStoreClass();
|
|
299
|
+
|
|
300
|
+
console.log('\n=== session中间件测试完成 ===');
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* 运行测试
|
|
305
|
+
*/
|
|
306
|
+
async function runTests() {
|
|
307
|
+
try {
|
|
308
|
+
await testSession();
|
|
309
|
+
} catch (err) {
|
|
310
|
+
console.error('测试执行出错:', err);
|
|
178
311
|
}
|
|
179
312
|
}
|
|
180
313
|
|
|
181
|
-
//
|
|
314
|
+
// 如果直接运行此文件,则执行测试
|
|
182
315
|
if (require.main === module) {
|
|
183
|
-
|
|
316
|
+
runTests();
|
|
184
317
|
}
|
|
185
318
|
|
|
319
|
+
setTimeout(() => {
|
|
320
|
+
process.exit(0);
|
|
321
|
+
}, 5000);
|
|
322
|
+
|
|
186
323
|
module.exports = {
|
|
187
324
|
MockContext,
|
|
188
|
-
|
|
189
|
-
testSession
|
|
325
|
+
mock_next,
|
|
326
|
+
testSession,
|
|
327
|
+
runTests
|
|
190
328
|
};
|