/** * RPC 验证器 * * 专门用于验证 RPC 调用的参数、权限、频率等 */ import { NetworkValue, RpcMetadata } from '@esengine/ecs-framework-network-shared'; import { ClientConnection } from '../core/ClientConnection'; import { ValidationResult } from './MessageValidator'; /** * RPC 验证配置 */ export interface RpcValidationConfig { /** 最大参数数量 */ maxParameterCount?: number; /** 单个参数最大大小(字节) */ maxParameterSize?: number; /** 允许的参数类型 */ allowedParameterTypes?: string[]; /** 方法名黑名单 */ blacklistedMethods?: string[]; /** 方法名白名单 */ whitelistedMethods?: string[]; /** 是否启用参数类型检查 */ enableTypeCheck?: boolean; /** 是否启用参数内容过滤 */ enableContentFilter?: boolean; } /** * RPC 调用上下文 */ export interface RpcCallContext { /** 客户端连接 */ client: ClientConnection; /** 网络对象ID */ networkId: number; /** 组件类型 */ componentType: string; /** 方法名 */ methodName: string; /** 参数列表 */ parameters: NetworkValue[]; /** RPC 元数据 */ metadata: RpcMetadata; /** RPC 类型 */ rpcType: 'client-rpc' | 'server-rpc'; } /** * 参数类型定义 */ export interface ParameterTypeDefinition { /** 参数名 */ name: string; /** 参数类型 */ type: 'string' | 'number' | 'boolean' | 'object' | 'array' | 'any'; /** 是否必需 */ required?: boolean; /** 最小值/长度 */ min?: number; /** 最大值/长度 */ max?: number; /** 允许的值列表 */ allowedValues?: NetworkValue[]; /** 正则表达式(仅用于字符串) */ pattern?: RegExp; /** 自定义验证函数 */ customValidator?: (value: NetworkValue) => ValidationResult; } /** * 方法签名定义 */ export interface MethodSignature { /** 方法名 */ methodName: string; /** 组件类型 */ componentType: string; /** 参数定义 */ parameters: ParameterTypeDefinition[]; /** 返回值类型 */ returnType?: string; /** 是否需要权限验证 */ requiresAuth?: boolean; /** 所需权限 */ requiredPermissions?: string[]; /** 频率限制(调用/分钟) */ rateLimit?: number; /** 自定义验证函数 */ customValidator?: (context: RpcCallContext) => ValidationResult; } /** * RPC 频率跟踪 */ interface RpcRateTracker { /** 客户端ID */ clientId: string; /** 方法调用计数 */ methodCalls: Map; /** 最后更新时间 */ lastUpdate: Date; } /** * RPC 验证器 */ export class RpcValidator { private config: RpcValidationConfig; private methodSignatures = new Map(); private rateTrackers = new Map(); private cleanupTimer: NodeJS.Timeout | null = null; constructor(config: RpcValidationConfig = {}) { this.config = { maxParameterCount: 10, maxParameterSize: 65536, // 64KB allowedParameterTypes: ['string', 'number', 'boolean', 'object', 'array'], blacklistedMethods: [], whitelistedMethods: [], enableTypeCheck: true, enableContentFilter: true, ...config }; this.initialize(); } /** * 验证 RPC 调用 */ validateRpcCall(context: RpcCallContext): ValidationResult { try { // 基本验证 const basicResult = this.validateBasicRpcCall(context); if (!basicResult.valid) { return basicResult; } // 方法名验证 const methodResult = this.validateMethodName(context); if (!methodResult.valid) { return methodResult; } // 权限验证 const permissionResult = this.validateRpcPermissions(context); if (!permissionResult.valid) { return permissionResult; } // 参数验证 const parameterResult = this.validateParameters(context); if (!parameterResult.valid) { return parameterResult; } // 频率限制验证 const rateResult = this.validateRateLimit(context); if (!rateResult.valid) { return rateResult; } // 签名验证(如果有定义) const signatureResult = this.validateMethodSignature(context); if (!signatureResult.valid) { return signatureResult; } return { valid: true }; } catch (error) { return { valid: false, error: (error as Error).message, errorCode: 'RPC_VALIDATION_ERROR' }; } } /** * 注册方法签名 */ registerMethodSignature(signature: MethodSignature): void { const key = `${signature.componentType}.${signature.methodName}`; this.methodSignatures.set(key, signature); } /** * 移除方法签名 */ removeMethodSignature(componentType: string, methodName: string): boolean { const key = `${componentType}.${methodName}`; return this.methodSignatures.delete(key); } /** * 获取方法签名 */ getMethodSignature(componentType: string, methodName: string): MethodSignature | undefined { const key = `${componentType}.${methodName}`; return this.methodSignatures.get(key); } /** * 添加方法到黑名单 */ addToBlacklist(methodName: string): void { if (!this.config.blacklistedMethods!.includes(methodName)) { this.config.blacklistedMethods!.push(methodName); } } /** * 从黑名单移除方法 */ removeFromBlacklist(methodName: string): boolean { const index = this.config.blacklistedMethods!.indexOf(methodName); if (index !== -1) { this.config.blacklistedMethods!.splice(index, 1); return true; } return false; } /** * 添加方法到白名单 */ addToWhitelist(methodName: string): void { if (!this.config.whitelistedMethods!.includes(methodName)) { this.config.whitelistedMethods!.push(methodName); } } /** * 获取客户端的 RPC 统计 */ getClientRpcStats(clientId: string): { totalCalls: number; methodStats: Record; } { const tracker = this.rateTrackers.get(clientId); if (!tracker) { return { totalCalls: 0, methodStats: {} }; } let totalCalls = 0; const methodStats: Record = {}; for (const [method, data] of tracker.methodCalls) { totalCalls += data.count; methodStats[method] = data.count; } return { totalCalls, methodStats }; } /** * 重置客户端的频率限制 */ resetClientRateLimit(clientId: string): boolean { const tracker = this.rateTrackers.get(clientId); if (!tracker) { return false; } tracker.methodCalls.clear(); tracker.lastUpdate = new Date(); return true; } /** * 销毁验证器 */ destroy(): void { if (this.cleanupTimer) { clearInterval(this.cleanupTimer); this.cleanupTimer = null; } this.methodSignatures.clear(); this.rateTrackers.clear(); } /** * 初始化验证器 */ private initialize(): void { // 启动清理定时器(每5分钟清理一次) this.cleanupTimer = setInterval(() => { this.cleanupRateTrackers(); }, 5 * 60 * 1000); } /** * 基本 RPC 调用验证 */ private validateBasicRpcCall(context: RpcCallContext): ValidationResult { // 网络对象ID验证 if (!Number.isInteger(context.networkId) || context.networkId <= 0) { return { valid: false, error: 'Invalid network object ID', errorCode: 'INVALID_NETWORK_ID' }; } // 组件类型验证 if (!context.componentType || typeof context.componentType !== 'string') { return { valid: false, error: 'Invalid component type', errorCode: 'INVALID_COMPONENT_TYPE' }; } // 方法名验证 if (!context.methodName || typeof context.methodName !== 'string') { return { valid: false, error: 'Invalid method name', errorCode: 'INVALID_METHOD_NAME' }; } // 参数数组验证 if (!Array.isArray(context.parameters)) { return { valid: false, error: 'Parameters must be an array', errorCode: 'INVALID_PARAMETERS_FORMAT' }; } // 参数数量检查 if (context.parameters.length > this.config.maxParameterCount!) { return { valid: false, error: `Too many parameters: ${context.parameters.length} (max: ${this.config.maxParameterCount})`, errorCode: 'TOO_MANY_PARAMETERS' }; } return { valid: true }; } /** * 方法名验证 */ private validateMethodName(context: RpcCallContext): ValidationResult { const methodName = context.methodName; // 黑名单检查 if (this.config.blacklistedMethods!.length > 0) { if (this.config.blacklistedMethods!.includes(methodName)) { return { valid: false, error: `Method '${methodName}' is blacklisted`, errorCode: 'METHOD_BLACKLISTED' }; } } // 白名单检查 if (this.config.whitelistedMethods!.length > 0) { if (!this.config.whitelistedMethods!.includes(methodName)) { return { valid: false, error: `Method '${methodName}' is not whitelisted`, errorCode: 'METHOD_NOT_WHITELISTED' }; } } // 危险方法名检查 const dangerousPatterns = [ /^__/, // 私有方法 /constructor/i, /prototype/i, /eval/i, /function/i ]; for (const pattern of dangerousPatterns) { if (pattern.test(methodName)) { return { valid: false, error: `Potentially dangerous method name: '${methodName}'`, errorCode: 'DANGEROUS_METHOD_NAME' }; } } return { valid: true }; } /** * RPC 权限验证 */ private validateRpcPermissions(context: RpcCallContext): ValidationResult { // 基本 RPC 权限检查 if (!context.client.hasPermission('canSendRpc')) { return { valid: false, error: 'Client does not have RPC permission', errorCode: 'RPC_PERMISSION_DENIED' }; } // ServerRpc 特殊权限检查 if (context.rpcType === 'server-rpc') { if (context.metadata.requiresAuth && !context.client.isAuthenticated) { return { valid: false, error: 'Authentication required for this RPC', errorCode: 'AUTHENTICATION_REQUIRED' }; } } // 检查方法签名中的权限要求 const signature = this.getMethodSignature(context.componentType, context.methodName); if (signature && signature.requiredPermissions) { for (const permission of signature.requiredPermissions) { if (!context.client.hasCustomPermission(permission)) { return { valid: false, error: `Required permission '${permission}' not found`, errorCode: 'INSUFFICIENT_PERMISSIONS' }; } } } return { valid: true }; } /** * 参数验证 */ private validateParameters(context: RpcCallContext): ValidationResult { // 参数大小检查 for (let i = 0; i < context.parameters.length; i++) { const param = context.parameters[i]; try { const serialized = JSON.stringify(param); const size = new TextEncoder().encode(serialized).length; if (size > this.config.maxParameterSize!) { return { valid: false, error: `Parameter ${i} is too large: ${size} bytes (max: ${this.config.maxParameterSize})`, errorCode: 'PARAMETER_TOO_LARGE' }; } } catch (error) { return { valid: false, error: `Parameter ${i} is not serializable`, errorCode: 'PARAMETER_NOT_SERIALIZABLE' }; } } // 参数类型检查 if (this.config.enableTypeCheck) { const typeResult = this.validateParameterTypes(context); if (!typeResult.valid) { return typeResult; } } // 参数内容过滤 if (this.config.enableContentFilter) { const contentResult = this.validateParameterContent(context); if (!contentResult.valid) { return contentResult; } } return { valid: true }; } /** * 参数类型验证 */ private validateParameterTypes(context: RpcCallContext): ValidationResult { for (let i = 0; i < context.parameters.length; i++) { const param = context.parameters[i]; const paramType = this.getParameterType(param); if (!this.config.allowedParameterTypes!.includes(paramType)) { return { valid: false, error: `Parameter ${i} type '${paramType}' is not allowed`, errorCode: 'INVALID_PARAMETER_TYPE' }; } } return { valid: true }; } /** * 参数内容验证 */ private validateParameterContent(context: RpcCallContext): ValidationResult { for (let i = 0; i < context.parameters.length; i++) { const param = context.parameters[i]; // 检查危险内容 if (typeof param === 'string') { const dangerousPatterns = [ /