Files
whale-town/scripts/RateLimiter.gd
2025-12-05 19:00:14 +08:00

230 lines
6.8 KiB
GDScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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