更新network库及core库优化

This commit is contained in:
YHH
2025-08-12 09:39:07 +08:00
parent c178e2fbcc
commit 9f76d37a82
117 changed files with 17988 additions and 4099 deletions

View File

@@ -0,0 +1,776 @@
/**
* 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<string, { count: number; resetTime: Date }>;
/** 最后更新时间 */
lastUpdate: Date;
}
/**
* RPC 验证器
*/
export class RpcValidator {
private config: RpcValidationConfig;
private methodSignatures = new Map<string, MethodSignature>();
private rateTrackers = new Map<string, RpcRateTracker>();
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<string, number>;
} {
const tracker = this.rateTrackers.get(clientId);
if (!tracker) {
return { totalCalls: 0, methodStats: {} };
}
let totalCalls = 0;
const methodStats: Record<string, number> = {};
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 = [
/<script/i,
/javascript:/i,
/eval\(/i,
/function\(/i,
/__proto__/i
];
for (const pattern of dangerousPatterns) {
if (pattern.test(param)) {
return {
valid: false,
error: `Parameter ${i} contains potentially dangerous content`,
errorCode: 'DANGEROUS_PARAMETER_CONTENT'
};
}
}
}
}
return { valid: true };
}
/**
* 频率限制验证
*/
private validateRateLimit(context: RpcCallContext): ValidationResult {
const signature = this.getMethodSignature(context.componentType, context.methodName);
if (!signature || !signature.rateLimit) {
return { valid: true }; // 没有频率限制
}
const clientId = context.client.id;
const methodKey = `${context.componentType}.${context.methodName}`;
let tracker = this.rateTrackers.get(clientId);
if (!tracker) {
tracker = {
clientId,
methodCalls: new Map(),
lastUpdate: new Date()
};
this.rateTrackers.set(clientId, tracker);
}
const now = new Date();
let methodData = tracker.methodCalls.get(methodKey);
if (!methodData) {
methodData = {
count: 1,
resetTime: new Date(now.getTime() + 60000) // 1分钟后重置
};
tracker.methodCalls.set(methodKey, methodData);
return { valid: true };
}
// 检查是否需要重置
if (now >= methodData.resetTime) {
methodData.count = 1;
methodData.resetTime = new Date(now.getTime() + 60000);
return { valid: true };
}
// 检查频率限制
if (methodData.count >= signature.rateLimit) {
return {
valid: false,
error: `Rate limit exceeded for method '${methodKey}': ${methodData.count}/${signature.rateLimit} per minute`,
errorCode: 'RATE_LIMIT_EXCEEDED'
};
}
methodData.count++;
tracker.lastUpdate = now;
return { valid: true };
}
/**
* 方法签名验证
*/
private validateMethodSignature(context: RpcCallContext): ValidationResult {
const signature = this.getMethodSignature(context.componentType, context.methodName);
if (!signature) {
return { valid: true }; // 没有定义签名,跳过验证
}
// 参数数量检查
const requiredParams = signature.parameters.filter(p => p.required !== false);
if (context.parameters.length < requiredParams.length) {
return {
valid: false,
error: `Not enough parameters: expected at least ${requiredParams.length}, got ${context.parameters.length}`,
errorCode: 'INSUFFICIENT_PARAMETERS'
};
}
if (context.parameters.length > signature.parameters.length) {
return {
valid: false,
error: `Too many parameters: expected at most ${signature.parameters.length}, got ${context.parameters.length}`,
errorCode: 'EXCESS_PARAMETERS'
};
}
// 参数类型和值验证
for (let i = 0; i < Math.min(context.parameters.length, signature.parameters.length); i++) {
const param = context.parameters[i];
const paramDef = signature.parameters[i];
const paramResult = this.validateParameterDefinition(param, paramDef, i);
if (!paramResult.valid) {
return paramResult;
}
}
// 自定义验证
if (signature.customValidator) {
const customResult = signature.customValidator(context);
if (!customResult.valid) {
return customResult;
}
}
return { valid: true };
}
/**
* 验证参数定义
*/
private validateParameterDefinition(
value: NetworkValue,
definition: ParameterTypeDefinition,
index: number
): ValidationResult {
// 类型检查
const actualType = this.getParameterType(value);
if (definition.type !== 'any' && actualType !== definition.type) {
return {
valid: false,
error: `Parameter ${index} type mismatch: expected '${definition.type}', got '${actualType}'`,
errorCode: 'PARAMETER_TYPE_MISMATCH'
};
}
// 值范围检查
if (typeof value === 'number' && (definition.min !== undefined || definition.max !== undefined)) {
if (definition.min !== undefined && value < definition.min) {
return {
valid: false,
error: `Parameter ${index} value ${value} is less than minimum ${definition.min}`,
errorCode: 'PARAMETER_BELOW_MINIMUM'
};
}
if (definition.max !== undefined && value > definition.max) {
return {
valid: false,
error: `Parameter ${index} value ${value} is greater than maximum ${definition.max}`,
errorCode: 'PARAMETER_ABOVE_MAXIMUM'
};
}
}
// 字符串长度检查
if (typeof value === 'string' && (definition.min !== undefined || definition.max !== undefined)) {
if (definition.min !== undefined && value.length < definition.min) {
return {
valid: false,
error: `Parameter ${index} string length ${value.length} is less than minimum ${definition.min}`,
errorCode: 'STRING_TOO_SHORT'
};
}
if (definition.max !== undefined && value.length > definition.max) {
return {
valid: false,
error: `Parameter ${index} string length ${value.length} is greater than maximum ${definition.max}`,
errorCode: 'STRING_TOO_LONG'
};
}
}
// 允许值检查
if (definition.allowedValues && definition.allowedValues.length > 0) {
if (!definition.allowedValues.includes(value)) {
return {
valid: false,
error: `Parameter ${index} value '${value}' is not in allowed values: ${definition.allowedValues.join(', ')}`,
errorCode: 'VALUE_NOT_ALLOWED'
};
}
}
// 正则表达式检查(字符串)
if (typeof value === 'string' && definition.pattern) {
if (!definition.pattern.test(value)) {
return {
valid: false,
error: `Parameter ${index} string '${value}' does not match required pattern`,
errorCode: 'PATTERN_MISMATCH'
};
}
}
// 自定义验证
if (definition.customValidator) {
const customResult = definition.customValidator(value);
if (!customResult.valid) {
return {
...customResult,
error: `Parameter ${index} validation failed: ${customResult.error}`
};
}
}
return { valid: true };
}
/**
* 获取参数类型
*/
private getParameterType(value: any): string {
if (value === null || value === undefined) {
return 'null';
}
if (Array.isArray(value)) {
return 'array';
}
return typeof value;
}
/**
* 清理过期的频率跟踪器
*/
private cleanupRateTrackers(): void {
const now = new Date();
const expireTime = 10 * 60 * 1000; // 10分钟
let cleanedCount = 0;
for (const [clientId, tracker] of this.rateTrackers.entries()) {
if (now.getTime() - tracker.lastUpdate.getTime() > expireTime) {
this.rateTrackers.delete(clientId);
cleanedCount++;
} else {
// 清理过期的方法调用记录
for (const [methodKey, methodData] of tracker.methodCalls.entries()) {
if (now >= methodData.resetTime) {
tracker.methodCalls.delete(methodKey);
}
}
}
}
if (cleanedCount > 0) {
console.log(`RPC validator cleanup: ${cleanedCount} rate trackers removed`);
}
}
}