创建新工程
This commit is contained in:
230
scripts/RateLimiter.gd
Normal file
230
scripts/RateLimiter.gd
Normal file
@@ -0,0 +1,230 @@
|
||||
extends Node
|
||||
class_name RateLimiter
|
||||
## 速率限制器
|
||||
## 防止消息洪水攻击和垃圾消息
|
||||
|
||||
# 速率限制配置
|
||||
var max_messages_per_window: int = 10
|
||||
var time_window: float = 1.0 # 1秒窗口
|
||||
var cleanup_interval: float = 60.0 # 1分钟清理间隔
|
||||
|
||||
# 客户端消息记录
|
||||
var client_message_history: Dictionary = {}
|
||||
|
||||
# 清理定时器
|
||||
var cleanup_timer: Timer
|
||||
|
||||
## 初始化速率限制器
|
||||
func _ready():
|
||||
"""初始化速率限制器"""
|
||||
# 从配置获取参数
|
||||
max_messages_per_window = SecurityConfig.get_config("network_security", "max_message_rate", 10)
|
||||
time_window = SecurityConfig.get_config("network_security", "rate_limit_window", 1.0)
|
||||
|
||||
# 设置清理定时器
|
||||
cleanup_timer = Timer.new()
|
||||
cleanup_timer.wait_time = cleanup_interval
|
||||
cleanup_timer.timeout.connect(_cleanup_old_records)
|
||||
cleanup_timer.autostart = true
|
||||
add_child(cleanup_timer)
|
||||
|
||||
print("RateLimiter initialized - Max: %d msgs/%s sec" % [max_messages_per_window, time_window])
|
||||
|
||||
## 检查是否允许消息
|
||||
func is_message_allowed(client_id: String) -> bool:
|
||||
"""
|
||||
检查客户端是否允许发送消息
|
||||
@param client_id: 客户端ID
|
||||
@return: 是否允许
|
||||
"""
|
||||
var current_time = Time.get_unix_time_from_system()
|
||||
|
||||
# 获取或创建客户端记录
|
||||
if not client_message_history.has(client_id):
|
||||
client_message_history[client_id] = {
|
||||
"messages": [],
|
||||
"last_cleanup": current_time
|
||||
}
|
||||
|
||||
var client_record = client_message_history[client_id]
|
||||
var messages = client_record.messages
|
||||
|
||||
# 清理过期消息
|
||||
_cleanup_client_messages(client_id, current_time)
|
||||
|
||||
# 检查是否超过限制
|
||||
if messages.size() >= max_messages_per_window:
|
||||
print("WARNING: Rate limit exceeded for client: " + client_id + " (" + str(messages.size()) + " messages)")
|
||||
return false
|
||||
|
||||
# 记录新消息
|
||||
messages.append(current_time)
|
||||
return true
|
||||
|
||||
## 清理客户端的过期消息记录
|
||||
func _cleanup_client_messages(client_id: String, current_time: float):
|
||||
"""
|
||||
清理客户端的过期消息记录
|
||||
@param client_id: 客户端ID
|
||||
@param current_time: 当前时间
|
||||
"""
|
||||
if not client_message_history.has(client_id):
|
||||
return
|
||||
|
||||
var client_record = client_message_history[client_id]
|
||||
var messages = client_record.messages
|
||||
var cutoff_time = current_time - time_window
|
||||
|
||||
# 移除过期消息
|
||||
var valid_messages = []
|
||||
for timestamp in messages:
|
||||
if timestamp > cutoff_time:
|
||||
valid_messages.append(timestamp)
|
||||
|
||||
client_record.messages = valid_messages
|
||||
client_record.last_cleanup = current_time
|
||||
|
||||
## 清理所有过期记录
|
||||
func _cleanup_old_records():
|
||||
"""清理所有客户端的过期记录"""
|
||||
var current_time = Time.get_unix_time_from_system()
|
||||
var cleaned_clients = 0
|
||||
var removed_clients = []
|
||||
|
||||
for client_id in client_message_history:
|
||||
var client_record = client_message_history[client_id]
|
||||
|
||||
# 如果客户端超过5分钟没有活动,移除记录
|
||||
if current_time - client_record.last_cleanup > 300.0:
|
||||
removed_clients.append(client_id)
|
||||
else:
|
||||
# 清理过期消息
|
||||
_cleanup_client_messages(client_id, current_time)
|
||||
cleaned_clients += 1
|
||||
|
||||
# 移除不活跃的客户端记录
|
||||
for client_id in removed_clients:
|
||||
client_message_history.erase(client_id)
|
||||
|
||||
if removed_clients.size() > 0:
|
||||
print("Rate limiter cleaned up %d inactive clients, %d active clients remain" % [removed_clients.size(), cleaned_clients])
|
||||
|
||||
## 重置客户端限制
|
||||
func reset_client_limit(client_id: String):
|
||||
"""
|
||||
重置客户端的速率限制(用于特殊情况)
|
||||
@param client_id: 客户端ID
|
||||
"""
|
||||
if client_message_history.has(client_id):
|
||||
client_message_history[client_id].messages.clear()
|
||||
print("Rate limit reset for client: " + client_id)
|
||||
|
||||
## 获取客户端消息统计
|
||||
func get_client_stats(client_id: String) -> Dictionary:
|
||||
"""
|
||||
获取客户端的消息统计
|
||||
@param client_id: 客户端ID
|
||||
@return: 统计信息
|
||||
"""
|
||||
if not client_message_history.has(client_id):
|
||||
return {
|
||||
"message_count": 0,
|
||||
"remaining_quota": max_messages_per_window,
|
||||
"window_reset_time": 0
|
||||
}
|
||||
|
||||
var current_time = Time.get_unix_time_from_system()
|
||||
_cleanup_client_messages(client_id, current_time)
|
||||
|
||||
var client_record = client_message_history[client_id]
|
||||
var message_count = client_record.messages.size()
|
||||
var remaining_quota = max(0, max_messages_per_window - message_count)
|
||||
|
||||
# 计算窗口重置时间
|
||||
var oldest_message_time = 0.0
|
||||
if client_record.messages.size() > 0:
|
||||
oldest_message_time = client_record.messages[0]
|
||||
|
||||
var window_reset_time = oldest_message_time + time_window
|
||||
|
||||
return {
|
||||
"message_count": message_count,
|
||||
"remaining_quota": remaining_quota,
|
||||
"window_reset_time": window_reset_time,
|
||||
"current_time": current_time
|
||||
}
|
||||
|
||||
## 获取全局统计
|
||||
func get_global_stats() -> Dictionary:
|
||||
"""
|
||||
获取全局速率限制统计
|
||||
@return: 全局统计信息
|
||||
"""
|
||||
var total_clients = client_message_history.size()
|
||||
var active_clients = 0
|
||||
var total_messages = 0
|
||||
var current_time = Time.get_unix_time_from_system()
|
||||
|
||||
for client_id in client_message_history:
|
||||
var client_record = client_message_history[client_id]
|
||||
if current_time - client_record.last_cleanup < 60.0: # 1分钟内活跃
|
||||
active_clients += 1
|
||||
total_messages += client_record.messages.size()
|
||||
|
||||
return {
|
||||
"total_clients": total_clients,
|
||||
"active_clients": active_clients,
|
||||
"total_messages_in_window": total_messages,
|
||||
"max_rate": max_messages_per_window,
|
||||
"time_window": time_window
|
||||
}
|
||||
|
||||
## 设置速率限制参数
|
||||
func set_rate_limit(max_messages: int, window_seconds: float):
|
||||
"""
|
||||
动态设置速率限制参数
|
||||
@param max_messages: 最大消息数
|
||||
@param window_seconds: 时间窗口(秒)
|
||||
"""
|
||||
max_messages_per_window = max_messages
|
||||
time_window = window_seconds
|
||||
|
||||
print("Rate limit updated - Max: %d msgs/%s sec" % [max_messages, window_seconds])
|
||||
|
||||
## 检查是否为可疑活动
|
||||
func is_suspicious_activity(client_id: String) -> bool:
|
||||
"""
|
||||
检查客户端是否有可疑活动
|
||||
@param client_id: 客户端ID
|
||||
@return: 是否可疑
|
||||
"""
|
||||
var stats = get_client_stats(client_id)
|
||||
|
||||
# 如果消息数量接近限制,认为可疑
|
||||
if stats.message_count >= max_messages_per_window * 0.8:
|
||||
return true
|
||||
|
||||
# 检查消息发送模式是否异常(过于规律可能是机器人)
|
||||
if client_message_history.has(client_id):
|
||||
var messages = client_message_history[client_id].messages
|
||||
if messages.size() >= 3:
|
||||
var intervals = []
|
||||
for i in range(1, messages.size()):
|
||||
intervals.append(messages[i] - messages[i-1])
|
||||
|
||||
# 如果所有间隔都非常相似(差异小于0.1秒),可能是机器人
|
||||
if intervals.size() >= 2:
|
||||
var avg_interval = 0.0
|
||||
for interval in intervals:
|
||||
avg_interval += interval
|
||||
avg_interval /= intervals.size()
|
||||
|
||||
var variance = 0.0
|
||||
for interval in intervals:
|
||||
variance += (interval - avg_interval) * (interval - avg_interval)
|
||||
variance /= intervals.size()
|
||||
|
||||
if variance < 0.01: # 方差很小,模式过于规律
|
||||
return true
|
||||
|
||||
return false
|
||||
Reference in New Issue
Block a user