mm_ip 1.0.0
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/LICENSE +21 -0
- package/index.js +684 -0
- package/package.json +26 -0
- package/test.js +238 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Admin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/index.js
ADDED
|
@@ -0,0 +1,684 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IP管理模块
|
|
3
|
+
* @file IP管理模块,支持白名单、黑名单管理,IP请求频率和次数记录
|
|
4
|
+
*/
|
|
5
|
+
require('mm_expand');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* IP管理类
|
|
9
|
+
* @class
|
|
10
|
+
*/
|
|
11
|
+
class Ip {
|
|
12
|
+
/**
|
|
13
|
+
* 创建IP管理实例
|
|
14
|
+
* @param {Object} config - 配置选项
|
|
15
|
+
*/
|
|
16
|
+
constructor(config) {
|
|
17
|
+
this._config = Object.assign({
|
|
18
|
+
max_white: 1000,
|
|
19
|
+
max_black: 1000,
|
|
20
|
+
check_interval: 60000, // 1分钟
|
|
21
|
+
max_req_per_min: 100,
|
|
22
|
+
auto_black_enable: true, // 启用自动高频拉黑
|
|
23
|
+
auto_black_threshold: 50, // 自动拉黑阈值(每分钟请求数)
|
|
24
|
+
auto_black_duration: 3600000, // 自动拉黑持续时间(1小时)
|
|
25
|
+
violation_enable: true, // 启用违规记录
|
|
26
|
+
violation_max_count: 10, // 最大违规次数
|
|
27
|
+
violation_reset_time: 86400000, // 违规记录重置时间(24小时)
|
|
28
|
+
violation_auto_black: true // 违规次数超限自动拉黑
|
|
29
|
+
}, config || {});
|
|
30
|
+
|
|
31
|
+
this._white = new Set();
|
|
32
|
+
this._black = new Set();
|
|
33
|
+
this._req_count = new Map();
|
|
34
|
+
this._req_time = new Map();
|
|
35
|
+
this._black_time = new Map(); // 记录拉黑时间
|
|
36
|
+
this._violation_count = new Map(); // 违规次数记录
|
|
37
|
+
this._violation_time = new Map(); // 违规时间记录
|
|
38
|
+
this._logger = $.log || console;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 设置日志器
|
|
44
|
+
* @param {Object} logger - 日志对象
|
|
45
|
+
*/
|
|
46
|
+
Ip.prototype.setup = function (logger) {
|
|
47
|
+
if (logger && typeof logger === 'object') {
|
|
48
|
+
this._logger = logger;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 日志记录
|
|
54
|
+
* @param {string} level - 日志级别(如:info, warn, error)
|
|
55
|
+
* @param {string} message - 日志消息
|
|
56
|
+
* @param {...*} args - 可选的日志参数
|
|
57
|
+
*/
|
|
58
|
+
Ip.prototype.logger = function (level, message, ...args) {
|
|
59
|
+
this._logger[level](`${this.constructor.name} ${message}`, ...args);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 添加IP到白名单
|
|
64
|
+
* @param {string} ip - IP地址
|
|
65
|
+
* @returns {boolean} 是否添加成功
|
|
66
|
+
* @throws {TypeError} 当IP非字符串时
|
|
67
|
+
*/
|
|
68
|
+
Ip.prototype.addWhite = function (ip) {
|
|
69
|
+
if (typeof ip !== 'string') throw new TypeError('ip must be string');
|
|
70
|
+
|
|
71
|
+
if (this.getAllWhite().length >= this._config.max_white) {
|
|
72
|
+
this.logger('warn', '白名单已满,无法添加IP:', ip);
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
this._white.add(ip);
|
|
77
|
+
this.logger('info', '添加白名单IP:', ip);
|
|
78
|
+
return true;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* 添加IP到黑名单
|
|
83
|
+
* @param {string} ip - IP地址
|
|
84
|
+
* @returns {boolean} 是否添加成功
|
|
85
|
+
* @throws {TypeError} 当IP非字符串时
|
|
86
|
+
*/
|
|
87
|
+
Ip.prototype.addBlack = function (ip) {
|
|
88
|
+
if (typeof ip !== 'string') throw new TypeError('ip must be string');
|
|
89
|
+
|
|
90
|
+
if (this.getAllBlack().length >= this._config.max_black) {
|
|
91
|
+
this.logger('warn', '黑名单已满,无法添加IP:', ip);
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
this._black.add(ip);
|
|
96
|
+
this.logger('info', '添加黑名单IP:', ip);
|
|
97
|
+
return true;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* 从白名单删除IP
|
|
102
|
+
* @param {string} ip - IP地址
|
|
103
|
+
* @returns {boolean} 是否删除成功
|
|
104
|
+
* @throws {TypeError} 当IP非字符串时
|
|
105
|
+
*/
|
|
106
|
+
Ip.prototype.delWhite = function (ip) {
|
|
107
|
+
if (typeof ip !== 'string') throw new TypeError('ip must be string');
|
|
108
|
+
|
|
109
|
+
const result = this._white.delete(ip);
|
|
110
|
+
if (result) {
|
|
111
|
+
this.logger('info', '删除白名单IP:', ip);
|
|
112
|
+
}
|
|
113
|
+
return result;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* 从黑名单删除IP
|
|
118
|
+
* @param {string} ip - IP地址
|
|
119
|
+
* @returns {boolean} 是否删除成功
|
|
120
|
+
* @throws {TypeError} 当IP非字符串时
|
|
121
|
+
*/
|
|
122
|
+
Ip.prototype.delBlack = function (ip) {
|
|
123
|
+
if (typeof ip !== 'string') throw new TypeError('ip must be string');
|
|
124
|
+
|
|
125
|
+
const result = this._black.delete(ip);
|
|
126
|
+
if (result) {
|
|
127
|
+
this.logger('info', '删除黑名单IP:', ip);
|
|
128
|
+
}
|
|
129
|
+
return result;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* 检查IP是否在白名单
|
|
134
|
+
* @param {string} ip - IP地址
|
|
135
|
+
* @returns {boolean} 是否在白名单
|
|
136
|
+
* @throws {TypeError} 当IP非字符串时
|
|
137
|
+
*/
|
|
138
|
+
Ip.prototype.isWhite = function (ip) {
|
|
139
|
+
if (typeof ip !== 'string') throw new TypeError('ip must be string');
|
|
140
|
+
return this._white.has(ip);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* 检查IP是否在黑名单
|
|
145
|
+
* @param {string} ip - IP地址
|
|
146
|
+
* @returns {boolean} 是否在黑名单
|
|
147
|
+
* @throws {TypeError} 当IP非字符串时
|
|
148
|
+
*/
|
|
149
|
+
Ip.prototype.isBlack = function (ip) {
|
|
150
|
+
if (typeof ip !== 'string') throw new TypeError('ip must be string');
|
|
151
|
+
return this._black.has(ip);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* 记录IP请求
|
|
156
|
+
* @param {string} ip - IP地址
|
|
157
|
+
* @returns {boolean} 是否允许请求
|
|
158
|
+
* @throws {TypeError} 当IP非字符串时
|
|
159
|
+
*/
|
|
160
|
+
Ip.prototype.record = function (ip) {
|
|
161
|
+
if (typeof ip !== 'string') throw new TypeError('ip must be string');
|
|
162
|
+
|
|
163
|
+
// 检查黑名单(包括过期检查)
|
|
164
|
+
if (this.isBlack(ip)) {
|
|
165
|
+
// 检查是否过期
|
|
166
|
+
if (this._isBlackExpired(ip)) {
|
|
167
|
+
this.delBlack(ip);
|
|
168
|
+
this.logger('info', '黑名单IP已过期,自动移除:', ip);
|
|
169
|
+
} else {
|
|
170
|
+
this.logger('warn', '黑名单IP请求被拒绝:', ip);
|
|
171
|
+
// 记录违规:黑名单IP尝试访问
|
|
172
|
+
if (this._config.violation_enable) {
|
|
173
|
+
this.recordViolation(ip, '黑名单IP尝试访问');
|
|
174
|
+
}
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// 白名单直接通过
|
|
180
|
+
if (this.isWhite(ip)) {
|
|
181
|
+
this._updateReqCount(ip);
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// 检查频率限制
|
|
186
|
+
if (!this._checkRateLimit(ip)) {
|
|
187
|
+
this.logger('warn', 'IP请求频率超限:', ip);
|
|
188
|
+
|
|
189
|
+
// 记录违规:请求频率超限
|
|
190
|
+
if (this._config.violation_enable) {
|
|
191
|
+
this.recordViolation(ip, '请求频率超限');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// 检查是否触发自动拉黑
|
|
195
|
+
if (this._config.auto_black_enable) {
|
|
196
|
+
const rate = this.getReqRate(ip);
|
|
197
|
+
if (rate >= this._config.auto_black_threshold) {
|
|
198
|
+
this._autoBlack(ip);
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
this._updateReqCount(ip);
|
|
207
|
+
return true;
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* 获取IP请求次数
|
|
212
|
+
* @param {string} ip - IP地址
|
|
213
|
+
* @returns {number} 请求次数
|
|
214
|
+
* @throws {TypeError} 当IP非字符串时
|
|
215
|
+
*/
|
|
216
|
+
Ip.prototype.getReqCount = function (ip) {
|
|
217
|
+
if (typeof ip !== 'string') throw new TypeError('ip must be string');
|
|
218
|
+
return this._req_count.get(ip) || 0;
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* 获取IP请求频率(每分钟请求数)
|
|
223
|
+
* @param {string} ip - IP地址
|
|
224
|
+
* @returns {number} 请求频率(每分钟请求数)
|
|
225
|
+
* @throws {TypeError} 当IP非字符串时
|
|
226
|
+
*/
|
|
227
|
+
Ip.prototype.getReqRate = function (ip) {
|
|
228
|
+
if (typeof ip !== 'string') throw new TypeError('ip must be string');
|
|
229
|
+
|
|
230
|
+
const now = Date.now();
|
|
231
|
+
const lastTime = this._req_time.get(ip) || now;
|
|
232
|
+
const count = this._req_count.get(ip) || 0;
|
|
233
|
+
|
|
234
|
+
if (now - lastTime < this._config.check_interval) {
|
|
235
|
+
// 在检查间隔内,计算实际频率
|
|
236
|
+
const elapsed = Math.max(1, now - lastTime); // 避免除零
|
|
237
|
+
return Math.round((count / elapsed) * this._config.check_interval);
|
|
238
|
+
} else {
|
|
239
|
+
// 超过检查间隔,频率为0
|
|
240
|
+
return 0;
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* 获取所有IP的请求统计信息
|
|
246
|
+
* @returns {Object} IP统计信息对象
|
|
247
|
+
*/
|
|
248
|
+
Ip.prototype.getAllStats = function () {
|
|
249
|
+
const stats = {};
|
|
250
|
+
const now = Date.now();
|
|
251
|
+
|
|
252
|
+
// 收集所有有请求记录的IP
|
|
253
|
+
for (const ip of this.getAllReqIps()) {
|
|
254
|
+
const lastTime = this._req_time.get(ip) || now;
|
|
255
|
+
const isActive = now - lastTime < this._config.check_interval;
|
|
256
|
+
|
|
257
|
+
stats[ip] = {
|
|
258
|
+
count: this.getReqCount(ip),
|
|
259
|
+
rate: isActive ? this.getReqRate(ip) : 0,
|
|
260
|
+
last_time: lastTime,
|
|
261
|
+
is_white: this.isWhite(ip),
|
|
262
|
+
is_black: this.isBlack(ip),
|
|
263
|
+
black_time: this.getBlackExpireTime(ip)
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return stats;
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* 获取所有白名单IP
|
|
272
|
+
* @returns {Array} 白名单IP数组
|
|
273
|
+
*/
|
|
274
|
+
Ip.prototype.getAllWhite = function () {
|
|
275
|
+
return Array.from(this._white);
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* 获取所有黑名单IP
|
|
280
|
+
* @returns {Array} 黑名单IP数组
|
|
281
|
+
*/
|
|
282
|
+
Ip.prototype.getAllBlack = function () {
|
|
283
|
+
return Array.from(this._black);
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* 获取所有有请求记录的IP
|
|
288
|
+
* @returns {Array} 有请求记录的IP数组
|
|
289
|
+
*/
|
|
290
|
+
Ip.prototype.getAllReqIps = function () {
|
|
291
|
+
return Array.from(this._req_count.keys());
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* 清空白名单
|
|
296
|
+
*/
|
|
297
|
+
Ip.prototype.clearWhite = function () {
|
|
298
|
+
this._white.clear();
|
|
299
|
+
this.logger('info', '清空白名单');
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* 清空黑名单
|
|
304
|
+
*/
|
|
305
|
+
Ip.prototype.clearBlack = function () {
|
|
306
|
+
this._black.clear();
|
|
307
|
+
this.logger('info', '清空黑名单');
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* 清空所有请求记录
|
|
312
|
+
*/
|
|
313
|
+
Ip.prototype.clearReq = function () {
|
|
314
|
+
this._req_count.clear();
|
|
315
|
+
this._req_time.clear();
|
|
316
|
+
this.logger('info', '清空请求记录');
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* 检查IP请求频率限制(私有方法)
|
|
321
|
+
* @private
|
|
322
|
+
* @param {string} ip - IP地址
|
|
323
|
+
* @returns {boolean} 是否允许请求
|
|
324
|
+
*/
|
|
325
|
+
Ip.prototype._checkRateLimit = function (ip) {
|
|
326
|
+
const now = Date.now();
|
|
327
|
+
const lastTime = this._req_time.get(ip) || 0;
|
|
328
|
+
|
|
329
|
+
// 检查时间间隔
|
|
330
|
+
if (now - lastTime < this._config.check_interval) {
|
|
331
|
+
const count = this.getReqCount(ip);
|
|
332
|
+
if (count >= this._config.max_req_per_min) {
|
|
333
|
+
return false;
|
|
334
|
+
}
|
|
335
|
+
} else {
|
|
336
|
+
// 重置计数
|
|
337
|
+
this._req_count.set(ip, 0);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
return true;
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* 更新IP请求计数(私有方法)
|
|
345
|
+
* @private
|
|
346
|
+
* @param {string} ip - IP地址
|
|
347
|
+
*/
|
|
348
|
+
Ip.prototype._updateReqCount = function (ip) {
|
|
349
|
+
const count = this.getReqCount(ip) + 1;
|
|
350
|
+
this._req_count.set(ip, count);
|
|
351
|
+
this._req_time.set(ip, Date.now());
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* 验证IP格式(私有方法)
|
|
356
|
+
* @private
|
|
357
|
+
* @param {string} ip - IP地址
|
|
358
|
+
* @returns {boolean} 是否有效IP
|
|
359
|
+
*/
|
|
360
|
+
Ip.prototype._validIp = function (ip) {
|
|
361
|
+
const ipRegex = /^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/;
|
|
362
|
+
return ipRegex.test(ip);
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* 批量添加IP到白名单
|
|
367
|
+
* @param {Array} ip_list - IP地址数组
|
|
368
|
+
* @returns {number} 成功添加的数量
|
|
369
|
+
*/
|
|
370
|
+
Ip.prototype.addWhiteBatch = function (ip_list) {
|
|
371
|
+
if (!Array.isArray(ip_list)) throw new TypeError('ip_list must be array');
|
|
372
|
+
|
|
373
|
+
let success = 0;
|
|
374
|
+
for (const ip of ip_list) {
|
|
375
|
+
if (typeof ip === 'string' && this._validIp(ip)) {
|
|
376
|
+
if (this.addWhite(ip)) {
|
|
377
|
+
success++;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return success;
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* 批量添加IP到黑名单
|
|
386
|
+
* @param {Array} ip_list - IP地址数组
|
|
387
|
+
* @returns {number} 成功添加的数量
|
|
388
|
+
*/
|
|
389
|
+
Ip.prototype.addBlackBatch = function (ip_list) {
|
|
390
|
+
if (!Array.isArray(ip_list)) throw new TypeError('ip_list must be array');
|
|
391
|
+
|
|
392
|
+
let success = 0;
|
|
393
|
+
for (const ip of ip_list) {
|
|
394
|
+
if (typeof ip === 'string' && this._validIp(ip)) {
|
|
395
|
+
if (this.addBlack(ip)) {
|
|
396
|
+
success++;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
return success;
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* 手动高频拉黑IP
|
|
405
|
+
* @param {string} ip - IP地址
|
|
406
|
+
* @param {number} duration - 拉黑持续时间(毫秒)
|
|
407
|
+
* @returns {boolean} 是否拉黑成功
|
|
408
|
+
* @throws {TypeError} 当IP非字符串时
|
|
409
|
+
*/
|
|
410
|
+
Ip.prototype.blackByHighFreq = function (ip, duration) {
|
|
411
|
+
if (typeof ip !== 'string') throw new TypeError('ip must be string');
|
|
412
|
+
|
|
413
|
+
const black_duration = duration || this._config.auto_black_duration;
|
|
414
|
+
|
|
415
|
+
if (this.addBlack(ip)) {
|
|
416
|
+
this._black_time.set(ip, Date.now() + black_duration);
|
|
417
|
+
this.logger('warn', '手动高频拉黑IP:', ip, '持续时间:', black_duration + 'ms');
|
|
418
|
+
return true;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
return false;
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* 检查并清理过期的黑名单IP(私有方法)
|
|
426
|
+
* @private
|
|
427
|
+
*/
|
|
428
|
+
Ip.prototype._cleanExpiredBlack = function () {
|
|
429
|
+
const now = Date.now();
|
|
430
|
+
const expired = [];
|
|
431
|
+
|
|
432
|
+
// 获取所有有过期时间的黑名单IP
|
|
433
|
+
for (const ip of this.getAllBlack()) {
|
|
434
|
+
const expireTime = this.getBlackExpireTime(ip);
|
|
435
|
+
if (expireTime && now > expireTime) {
|
|
436
|
+
expired.push(ip);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
for (const ip of expired) {
|
|
441
|
+
this.delBlack(ip);
|
|
442
|
+
this._black_time.delete(ip);
|
|
443
|
+
this.logger('info', '清理过期黑名单IP:', ip);
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* 检查黑名单是否过期(私有方法)
|
|
449
|
+
* @private
|
|
450
|
+
* @param {string} ip - IP地址
|
|
451
|
+
* @returns {boolean} 是否过期
|
|
452
|
+
*/
|
|
453
|
+
Ip.prototype._isBlackExpired = function (ip) {
|
|
454
|
+
const expireTime = this.getBlackExpireTime(ip);
|
|
455
|
+
if (!expireTime) return false; // 没有设置过期时间,永不过期
|
|
456
|
+
|
|
457
|
+
return Date.now() > expireTime;
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* 自动拉黑IP(私有方法)
|
|
462
|
+
* @private
|
|
463
|
+
* @param {string} ip - IP地址
|
|
464
|
+
*/
|
|
465
|
+
Ip.prototype._autoBlack = function (ip) {
|
|
466
|
+
if (this.addBlack(ip)) {
|
|
467
|
+
this._black_time.set(ip, Date.now() + this._config.auto_black_duration);
|
|
468
|
+
this.logger('warn', '自动高频拉黑IP:', ip,
|
|
469
|
+
'频率:', this.getReqRate(ip) + '/分钟',
|
|
470
|
+
'持续时间:', this._config.auto_black_duration + 'ms');
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* 获取黑名单过期时间
|
|
476
|
+
* @param {string} ip - IP地址
|
|
477
|
+
* @returns {number|null} 过期时间戳(毫秒),null表示永不过期
|
|
478
|
+
* @throws {TypeError} 当IP非字符串时
|
|
479
|
+
*/
|
|
480
|
+
Ip.prototype.getBlackExpireTime = function (ip) {
|
|
481
|
+
if (typeof ip !== 'string') throw new TypeError('ip must be string');
|
|
482
|
+
|
|
483
|
+
if (!this.isBlack(ip)) return null;
|
|
484
|
+
|
|
485
|
+
const expireTime = this._black_time.get(ip);
|
|
486
|
+
return expireTime || null;
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* 清理所有过期的黑名单IP
|
|
491
|
+
* @returns {number} 清理的数量
|
|
492
|
+
*/
|
|
493
|
+
Ip.prototype.cleanExpiredBlack = function () {
|
|
494
|
+
const beforeCount = this.getAllBlack().length;
|
|
495
|
+
this._cleanExpiredBlack();
|
|
496
|
+
return beforeCount - this.getAllBlack().length;
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* 记录IP违规。
|
|
501
|
+
* @param {string} ip - IP地址
|
|
502
|
+
* @param {string} reason - 违规原因
|
|
503
|
+
* @returns {number} 当前违规次数
|
|
504
|
+
*/
|
|
505
|
+
Ip.prototype.recordViolation = function(ip, reason) {
|
|
506
|
+
if (typeof ip !== 'string') throw new TypeError('ip must be string');
|
|
507
|
+
if (!this._config.violation_enable) return 0;
|
|
508
|
+
|
|
509
|
+
this._cleanExpiredViolation();
|
|
510
|
+
|
|
511
|
+
const now = Date.now();
|
|
512
|
+
const count = this.getViolationCount(ip) + 1;
|
|
513
|
+
|
|
514
|
+
this._violation_count.set(ip, count);
|
|
515
|
+
this._violation_time.set(ip, now);
|
|
516
|
+
|
|
517
|
+
this.logger('info', 'IP违规记录:', { ip, reason, count, time: new Date(now).toISOString() });
|
|
518
|
+
|
|
519
|
+
// 检查是否达到自动拉黑条件
|
|
520
|
+
if (this._config.violation_auto_black && count >= this._config.violation_max_count) {
|
|
521
|
+
this.blackByHighFreq(ip, this._config.auto_black_duration);
|
|
522
|
+
this.logger('warn', 'IP因违规次数超限自动拉黑:', { ip, count, duration: this._config.auto_black_duration });
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
return count;
|
|
526
|
+
};
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* 获取IP违规次数。
|
|
530
|
+
* @param {string} ip - IP地址
|
|
531
|
+
* @returns {number} 违规次数
|
|
532
|
+
*/
|
|
533
|
+
Ip.prototype.getViolationCount = function(ip) {
|
|
534
|
+
if (typeof ip !== 'string') throw new TypeError('ip must be string');
|
|
535
|
+
|
|
536
|
+
this._cleanExpiredViolation();
|
|
537
|
+
return this._violation_count.get(ip) || 0;
|
|
538
|
+
};
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* 重置IP违规记录。
|
|
542
|
+
* @param {string} ip - IP地址
|
|
543
|
+
* @returns {boolean} 是否成功重置
|
|
544
|
+
*/
|
|
545
|
+
Ip.prototype.resetViolation = function(ip) {
|
|
546
|
+
if (typeof ip !== 'string') throw new TypeError('ip must be string');
|
|
547
|
+
|
|
548
|
+
const hadRecord = this._violation_count.has(ip);
|
|
549
|
+
this._violation_count.delete(ip);
|
|
550
|
+
this._violation_time.delete(ip);
|
|
551
|
+
|
|
552
|
+
return hadRecord;
|
|
553
|
+
};
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* 获取所有有违规记录的IP
|
|
557
|
+
* @returns {Array} 有违规记录的IP数组
|
|
558
|
+
*/
|
|
559
|
+
Ip.prototype.getAllViolationIps = function() {
|
|
560
|
+
return Array.from(this._violation_count.keys());
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* 获取IP违规详细信息
|
|
565
|
+
* @param {string} ip - IP地址
|
|
566
|
+
* @returns {Object|null} 违规信息
|
|
567
|
+
*/
|
|
568
|
+
Ip.prototype.getViolationInfo = function(ip) {
|
|
569
|
+
if (typeof ip !== 'string') throw new TypeError('ip must be string');
|
|
570
|
+
|
|
571
|
+
const count = this.getViolationCount(ip);
|
|
572
|
+
if (count === 0) return null;
|
|
573
|
+
|
|
574
|
+
return {
|
|
575
|
+
count: count,
|
|
576
|
+
time: this._violation_time.get(ip)
|
|
577
|
+
};
|
|
578
|
+
};
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* 获取所有IP违规统计信息。
|
|
582
|
+
* @returns {Object} 违规统计信息
|
|
583
|
+
*/
|
|
584
|
+
Ip.prototype.getAllViolations = function() {
|
|
585
|
+
this._cleanExpiredViolation();
|
|
586
|
+
|
|
587
|
+
const violations = {};
|
|
588
|
+
for (const ip of this.getAllViolationIps()) {
|
|
589
|
+
violations[ip] = this.getViolationInfo(ip);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
return violations;
|
|
593
|
+
};
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* 清理过期的违规记录(私有方法)
|
|
597
|
+
* @private
|
|
598
|
+
*/
|
|
599
|
+
Ip.prototype._cleanExpiredViolation = function() {
|
|
600
|
+
const now = Date.now();
|
|
601
|
+
const expired = [];
|
|
602
|
+
|
|
603
|
+
// 直接访问内部数据结构避免递归调用
|
|
604
|
+
for (const [ip, time] of this._violation_time) {
|
|
605
|
+
if (now - time > this._config.violation_reset_time) {
|
|
606
|
+
expired.push(ip);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
for (const ip of expired) {
|
|
611
|
+
this._violation_count.delete(ip);
|
|
612
|
+
this._violation_time.delete(ip);
|
|
613
|
+
}
|
|
614
|
+
};
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* 清空所有违规记录
|
|
618
|
+
*/
|
|
619
|
+
Ip.prototype.clearViolations = function() {
|
|
620
|
+
this._violation_count.clear();
|
|
621
|
+
this._violation_time.clear();
|
|
622
|
+
this.logger('info', '清空所有违规记录');
|
|
623
|
+
};
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* 删除IP记录(从黑名单、白名单、临时记录中删除)
|
|
627
|
+
* @param {string} ip - IP地址
|
|
628
|
+
* @returns {Object} 删除结果
|
|
629
|
+
* @throws {TypeError} 当IP非字符串时
|
|
630
|
+
*/
|
|
631
|
+
Ip.prototype.del = function(ip) {
|
|
632
|
+
if (typeof ip !== 'string') throw new TypeError('ip must be string');
|
|
633
|
+
|
|
634
|
+
const result = {
|
|
635
|
+
black: false,
|
|
636
|
+
white: false,
|
|
637
|
+
violation: false,
|
|
638
|
+
black_time: false,
|
|
639
|
+
violation_count: false,
|
|
640
|
+
violation_time: false
|
|
641
|
+
};
|
|
642
|
+
|
|
643
|
+
// 从黑名单删除
|
|
644
|
+
if (this._black.has(ip)) {
|
|
645
|
+
this._black.delete(ip);
|
|
646
|
+
result.black = true;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// 从白名单删除
|
|
650
|
+
if (this._white.has(ip)) {
|
|
651
|
+
this._white.delete(ip);
|
|
652
|
+
result.white = true;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// 从违规记录删除
|
|
656
|
+
if (this._violation_count.has(ip)) {
|
|
657
|
+
this._violation_count.delete(ip);
|
|
658
|
+
result.violation_count = true;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
if (this._violation_time.has(ip)) {
|
|
662
|
+
this._violation_time.delete(ip);
|
|
663
|
+
result.violation_time = true;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
// 从黑名单过期时间记录删除
|
|
667
|
+
if (this._black_time.has(ip)) {
|
|
668
|
+
this._black_time.delete(ip);
|
|
669
|
+
result.black_time = true;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
// 如果有任何删除操作,记录日志
|
|
673
|
+
if (Object.values(result).some(Boolean)) {
|
|
674
|
+
this.logger('info', '删除IP记录:', { ip, result });
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
return result;
|
|
678
|
+
};
|
|
679
|
+
|
|
680
|
+
if (global.$ && !$.ip) {
|
|
681
|
+
$.ip = new Ip();
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
module.exports = { Ip };
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mm_ip",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "用于管理IP名单,可设置白名单、黑名单和IP检查,查询IP请求次数、频率等。",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "node ./test.js"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://gitee.com/qiuwenwu91/mm_ip.git"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"IP管理",
|
|
15
|
+
"白名单",
|
|
16
|
+
"黑名单",
|
|
17
|
+
"IP",
|
|
18
|
+
"IP请求次数",
|
|
19
|
+
"IP请求频率"
|
|
20
|
+
],
|
|
21
|
+
"author": "qww",
|
|
22
|
+
"license": "ISC",
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"mm_expand": "^1.9.7"
|
|
25
|
+
}
|
|
26
|
+
}
|
package/test.js
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IP管理模块测试文件
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const { Ip } = require('./index.js');
|
|
6
|
+
|
|
7
|
+
// 创建自定义日志器
|
|
8
|
+
const logger = {
|
|
9
|
+
info: (...args) => console.log('[INFO]', ...args),
|
|
10
|
+
warn: (...args) => console.log('[WARN]', ...args),
|
|
11
|
+
error: (...args) => console.log('[ERROR]', ...args)
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// 创建IP管理实例(启用高频拉黑和违规记录)
|
|
15
|
+
const ip_manager = new Ip({
|
|
16
|
+
max_white: 5,
|
|
17
|
+
max_black: 5,
|
|
18
|
+
check_interval: 1000, // 1秒
|
|
19
|
+
max_req_per_min: 3,
|
|
20
|
+
auto_black_enable: true,
|
|
21
|
+
auto_black_threshold: 5, // 降低阈值便于测试
|
|
22
|
+
auto_black_duration: 5000, // 5秒便于测试
|
|
23
|
+
violation_enable: true, // 启用违规记录
|
|
24
|
+
violation_max_count: 3, // 降低阈值便于测试
|
|
25
|
+
violation_reset_time: 10000, // 10秒便于测试
|
|
26
|
+
violation_auto_black: true // 违规次数超限自动拉黑
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// 设置自定义日志器
|
|
30
|
+
ip_manager.setup(logger);
|
|
31
|
+
|
|
32
|
+
console.log('=== IP管理模块测试开始 ===\n');
|
|
33
|
+
|
|
34
|
+
// 测试1: 添加白名单
|
|
35
|
+
console.log('1. 测试添加白名单:');
|
|
36
|
+
console.log('添加 192.168.1.1:', ip_manager.addWhite('192.168.1.1'));
|
|
37
|
+
console.log('添加 192.168.1.2:', ip_manager.addWhite('192.168.1.2'));
|
|
38
|
+
console.log('添加 192.168.1.3:', ip_manager.addWhite('192.168.1.3'));
|
|
39
|
+
console.log('白名单列表:', ip_manager.getAllWhite());
|
|
40
|
+
|
|
41
|
+
// 测试2: 添加黑名单
|
|
42
|
+
console.log('\n2. 测试添加黑名单:');
|
|
43
|
+
console.log('添加 10.0.0.1:', ip_manager.addBlack('10.0.0.1'));
|
|
44
|
+
console.log('添加 10.0.0.2:', ip_manager.addBlack('10.0.0.2'));
|
|
45
|
+
console.log('黑名单列表:', ip_manager.getAllBlack());
|
|
46
|
+
|
|
47
|
+
// 测试3: 检查IP状态
|
|
48
|
+
console.log('\n3. 测试检查IP状态:');
|
|
49
|
+
console.log('192.168.1.1 在白名单:', ip_manager.isWhite('192.168.1.1'));
|
|
50
|
+
console.log('10.0.0.1 在黑名单:', ip_manager.isBlack('10.0.0.1'));
|
|
51
|
+
console.log('192.168.1.100 在白名单:', ip_manager.isWhite('192.168.1.100'));
|
|
52
|
+
|
|
53
|
+
// 测试4: 记录IP请求
|
|
54
|
+
console.log('\n4. 测试记录IP请求:');
|
|
55
|
+
console.log('白名单IP 192.168.1.1 请求:', ip_manager.record('192.168.1.1'));
|
|
56
|
+
console.log('黑名单IP 10.0.0.1 请求:', ip_manager.record('10.0.0.1'));
|
|
57
|
+
console.log('普通IP 172.16.0.1 请求:', ip_manager.record('172.16.0.1'));
|
|
58
|
+
|
|
59
|
+
// 测试5: 测试频率限制
|
|
60
|
+
console.log('\n5. 测试频率限制:');
|
|
61
|
+
for (let i = 0; i < 5; i++) {
|
|
62
|
+
const result = ip_manager.record('172.16.0.2');
|
|
63
|
+
console.log(`第${i+1}次请求 172.16.0.2:`, result);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 测试6: 获取请求次数
|
|
67
|
+
console.log('\n6. 获取请求次数:');
|
|
68
|
+
console.log('192.168.1.1 请求次数:', ip_manager.getReqCount('192.168.1.1'));
|
|
69
|
+
console.log('172.16.0.1 请求次数:', ip_manager.getReqCount('172.16.0.1'));
|
|
70
|
+
console.log('172.16.0.2 请求次数:', ip_manager.getReqCount('172.16.0.2'));
|
|
71
|
+
|
|
72
|
+
// 测试7: 批量操作
|
|
73
|
+
console.log('\n7. 测试批量操作:');
|
|
74
|
+
const white_list = ['192.168.1.4', '192.168.1.5', '192.168.1.6'];
|
|
75
|
+
const black_list = ['10.0.0.3', '10.0.0.4', '10.0.0.5'];
|
|
76
|
+
console.log('批量添加白名单成功数:', ip_manager.addWhiteBatch(white_list));
|
|
77
|
+
console.log('批量添加黑名单成功数:', ip_manager.addBlackBatch(black_list));
|
|
78
|
+
console.log('当前白名单:', ip_manager.getAllWhite());
|
|
79
|
+
console.log('当前黑名单:', ip_manager.getAllBlack());
|
|
80
|
+
|
|
81
|
+
// 测试8: 删除操作
|
|
82
|
+
console.log('\n8. 测试删除操作:');
|
|
83
|
+
console.log('删除白名单 192.168.1.1:', ip_manager.delWhite('192.168.1.1'));
|
|
84
|
+
console.log('删除黑名单 10.0.0.1:', ip_manager.delBlack('10.0.0.1'));
|
|
85
|
+
console.log('删除后白名单:', ip_manager.getAllWhite());
|
|
86
|
+
console.log('删除后黑名单:', ip_manager.getAllBlack());
|
|
87
|
+
|
|
88
|
+
// 测试9: 错误处理
|
|
89
|
+
console.log('\n9. 测试错误处理:');
|
|
90
|
+
try {
|
|
91
|
+
ip_manager.addWhite(123); // 非字符串IP
|
|
92
|
+
} catch (err) {
|
|
93
|
+
console.log('捕获错误:', err.message);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
ip_manager.addWhiteBatch('not_array'); // 非数组参数
|
|
98
|
+
} catch (err) {
|
|
99
|
+
console.log('捕获错误:', err.message);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 测试10: 清空操作
|
|
103
|
+
console.log('\n10. 测试清空操作:');
|
|
104
|
+
ip_manager.clearWhite();
|
|
105
|
+
ip_manager.clearBlack();
|
|
106
|
+
ip_manager.clearReq();
|
|
107
|
+
console.log('清空后白名单:', ip_manager.getAllWhite());
|
|
108
|
+
console.log('清空后黑名单:', ip_manager.getAllBlack());
|
|
109
|
+
|
|
110
|
+
// 测试11: 高频拉黑功能
|
|
111
|
+
console.log('\n11. 测试高频拉黑功能:');
|
|
112
|
+
|
|
113
|
+
// 模拟高频请求触发自动拉黑
|
|
114
|
+
console.log('模拟高频请求触发自动拉黑:');
|
|
115
|
+
for (let i = 0; i < 10; i++) {
|
|
116
|
+
const result = ip_manager.record('192.168.100.1');
|
|
117
|
+
console.log(`第${i+1}次请求 192.168.100.1:`, result);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
console.log('192.168.100.1 请求频率:', ip_manager.getReqRate('192.168.100.1') + '/分钟');
|
|
121
|
+
console.log('192.168.100.1 是否在黑名单:', ip_manager.isBlack('192.168.100.1'));
|
|
122
|
+
console.log('192.168.100.1 黑名单过期时间:', ip_manager.getBlackExpireTime('192.168.100.1'));
|
|
123
|
+
|
|
124
|
+
// 测试手动高频拉黑
|
|
125
|
+
console.log('\n测试手动高频拉黑:');
|
|
126
|
+
console.log('手动拉黑 192.168.100.2:', ip_manager.blackByHighFreq('192.168.100.2', 10000));
|
|
127
|
+
console.log('192.168.100.2 是否在黑名单:', ip_manager.isBlack('192.168.100.2'));
|
|
128
|
+
console.log('192.168.100.2 黑名单过期时间:', ip_manager.getBlackExpireTime('192.168.100.2'));
|
|
129
|
+
|
|
130
|
+
// 测试请求频率查询
|
|
131
|
+
console.log('\n测试请求频率查询:');
|
|
132
|
+
console.log('192.168.100.1 请求次数:', ip_manager.getReqCount('192.168.100.1'));
|
|
133
|
+
console.log('192.168.100.1 请求频率:', ip_manager.getReqRate('192.168.100.1') + '次/分钟');
|
|
134
|
+
|
|
135
|
+
// 测试获取所有统计信息
|
|
136
|
+
console.log('\n测试获取所有统计信息:');
|
|
137
|
+
const stats = ip_manager.getAllStats();
|
|
138
|
+
console.log('统计信息中的IP数量:', Object.keys(stats).length);
|
|
139
|
+
for (const ip in stats) {
|
|
140
|
+
if (ip === '192.168.100.1' || ip === '192.168.100.2') {
|
|
141
|
+
console.log(`${ip} 统计:`, stats[ip]);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// 测试过期清理
|
|
146
|
+
console.log('\n测试过期清理:');
|
|
147
|
+
console.log('清理前黑名单数量:', ip_manager.getAllBlack().length);
|
|
148
|
+
setTimeout(() => {
|
|
149
|
+
console.log('等待6秒后清理过期黑名单...');
|
|
150
|
+
const cleaned = ip_manager.cleanExpiredBlack();
|
|
151
|
+
console.log('清理过期黑名单数量:', cleaned);
|
|
152
|
+
console.log('清理后黑名单数量:', ip_manager.getAllBlack().length);
|
|
153
|
+
|
|
154
|
+
// 测试12: 违规记录功能
|
|
155
|
+
console.log('\n12. 测试违规记录功能:');
|
|
156
|
+
|
|
157
|
+
// 测试黑名单IP尝试访问记录违规
|
|
158
|
+
console.log('测试黑名单IP尝试访问记录违规:');
|
|
159
|
+
ip_manager.addBlack('192.168.200.1');
|
|
160
|
+
console.log('黑名单IP 192.168.200.1 尝试访问:', ip_manager.record('192.168.200.1'));
|
|
161
|
+
console.log('192.168.200.1 违规次数:', ip_manager.getViolationCount('192.168.200.1'));
|
|
162
|
+
|
|
163
|
+
// 测试频率超限记录违规
|
|
164
|
+
console.log('\n测试频率超限记录违规:');
|
|
165
|
+
for (let i = 0; i < 4; i++) {
|
|
166
|
+
const result = ip_manager.record('192.168.200.2');
|
|
167
|
+
console.log(`第${i+1}次请求 192.168.200.2:`, result);
|
|
168
|
+
}
|
|
169
|
+
console.log('192.168.200.2 违规次数:', ip_manager.getViolationCount('192.168.200.2'));
|
|
170
|
+
|
|
171
|
+
// 测试手动记录违规
|
|
172
|
+
console.log('\n测试手动记录违规:');
|
|
173
|
+
console.log('手动记录 192.168.200.3 违规:', ip_manager.recordViolation('192.168.200.3', '手动测试违规'));
|
|
174
|
+
console.log('再次记录 192.168.200.3 违规:', ip_manager.recordViolation('192.168.200.3', '再次违规'));
|
|
175
|
+
console.log('192.168.200.3 违规次数:', ip_manager.getViolationCount('192.168.200.3'));
|
|
176
|
+
|
|
177
|
+
// 测试违规次数超限自动拉黑
|
|
178
|
+
console.log('\n测试违规次数超限自动拉黑:');
|
|
179
|
+
console.log('第3次记录 192.168.200.3 违规:', ip_manager.recordViolation('192.168.200.3', '第三次违规'));
|
|
180
|
+
console.log('192.168.200.3 是否在黑名单:', ip_manager.isBlack('192.168.200.3'));
|
|
181
|
+
console.log('192.168.200.3 违规次数:', ip_manager.getViolationCount('192.168.200.3'));
|
|
182
|
+
|
|
183
|
+
// 测试获取所有违规记录
|
|
184
|
+
console.log('\n测试获取所有违规记录:');
|
|
185
|
+
const violations = ip_manager.getAllViolations();
|
|
186
|
+
console.log('违规记录数量:', Object.keys(violations).length);
|
|
187
|
+
for (const ip in violations) {
|
|
188
|
+
console.log(`${ip} 违规记录:`, violations[ip]);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// 测试重置违规记录
|
|
192
|
+
console.log('\n测试重置违规记录:');
|
|
193
|
+
console.log('重置 192.168.200.2 违规记录:', ip_manager.resetViolation('192.168.200.2'));
|
|
194
|
+
console.log('重置后 192.168.200.2 违规次数:', ip_manager.getViolationCount('192.168.200.2'));
|
|
195
|
+
|
|
196
|
+
// 测试清空所有违规记录
|
|
197
|
+
console.log('\n测试清空所有违规记录:');
|
|
198
|
+
ip_manager.clearViolations();
|
|
199
|
+
console.log('清空后违规记录数量:', Object.keys(ip_manager.getAllViolations()).length);
|
|
200
|
+
|
|
201
|
+
// 测试违规记录过期清理
|
|
202
|
+
console.log('\n测试违规记录过期清理:');
|
|
203
|
+
console.log('等待12秒后检查违规记录过期清理...');
|
|
204
|
+
setTimeout(() => {
|
|
205
|
+
console.log('过期清理后违规记录数量:', Object.keys(ip_manager.getAllViolations()).length);
|
|
206
|
+
|
|
207
|
+
// 测试13: 删除IP记录功能
|
|
208
|
+
console.log('\n13. 测试删除IP记录功能:');
|
|
209
|
+
|
|
210
|
+
// 准备测试数据
|
|
211
|
+
ip_manager.addBlack('192.168.300.1');
|
|
212
|
+
ip_manager.addWhite('192.168.300.1'); // 同时存在于黑白名单
|
|
213
|
+
ip_manager.recordViolation('192.168.300.1', '测试违规');
|
|
214
|
+
ip_manager.blackByHighFreq('192.168.300.1', 10000); // 设置过期时间
|
|
215
|
+
|
|
216
|
+
console.log('删除前状态:');
|
|
217
|
+
console.log('192.168.300.1 在黑名单:', ip_manager.isBlack('192.168.300.1'));
|
|
218
|
+
console.log('192.168.300.1 在白名单:', ip_manager.isWhite('192.168.300.1'));
|
|
219
|
+
console.log('192.168.300.1 违规次数:', ip_manager.getViolationCount('192.168.300.1'));
|
|
220
|
+
console.log('192.168.300.1 黑名单过期时间:', ip_manager.getBlackExpireTime('192.168.300.1'));
|
|
221
|
+
|
|
222
|
+
// 测试删除功能
|
|
223
|
+
const delResult = ip_manager.del('192.168.300.1');
|
|
224
|
+
console.log('删除结果:', delResult);
|
|
225
|
+
|
|
226
|
+
console.log('删除后状态:');
|
|
227
|
+
console.log('192.168.300.1 在黑名单:', ip_manager.isBlack('192.168.300.1'));
|
|
228
|
+
console.log('192.168.300.1 在白名单:', ip_manager.isWhite('192.168.300.1'));
|
|
229
|
+
console.log('192.168.300.1 违规次数:', ip_manager.getViolationCount('192.168.300.1'));
|
|
230
|
+
console.log('192.168.300.1 黑名单过期时间:', ip_manager.getBlackExpireTime('192.168.300.1'));
|
|
231
|
+
|
|
232
|
+
// 测试删除不存在的IP
|
|
233
|
+
const delNonExist = ip_manager.del('192.168.999.999');
|
|
234
|
+
console.log('删除不存在IP的结果:', delNonExist);
|
|
235
|
+
|
|
236
|
+
console.log('\n=== IP管理模块测试结束 ===');
|
|
237
|
+
}, 12000);
|
|
238
|
+
}, 6000);
|