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