use reqwest::Client; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::time::Duration; use base64::{Engine as _, engine::general_purpose}; use rsa::{RsaPublicKey, pkcs1::DecodeRsaPublicKey, Oaep, sha2::Sha256}; use rsa::pkcs8::DecodePublicKey; use openssl::symm::{encrypt, Cipher as OpenSSLCipher}; use rand::rngs::OsRng; use rand::RngCore; use openssl::pkey::PKey; use openssl::sign::Verifier; use openssl::hash::MessageDigest; use openssl::rsa::Padding; use crate::error::{ClientError, network_error, serialization_error, rsa_error, aes_error, config_error}; use crate::machine::generate_machine_code; #[derive(Debug, Serialize, Deserialize, Clone)] pub struct ApiConfig { pub base_url: String, pub timeout: u64, pub software_code: String, } impl Default for ApiConfig { fn default() -> Self { ApiConfig { base_url: "http://125.122.32.114:9000".to_string(), timeout: 30, software_code: "buff-xp-steam-sh".to_string(), } } } #[derive(Debug, Serialize, Deserialize)] pub struct ApiResponse { pub code: i32, pub msg: String, pub data: Option, } #[derive(Debug, Serialize, Deserialize)] pub struct UserInfo { #[serde(rename = "user_id")] pub id: Option, pub username: String, #[serde(default)] pub email: Option, #[serde(default)] pub expire_time: Option, #[serde(default)] pub is_active: Option, #[serde(default)] pub session_id: Option, } #[derive(Debug, Serialize, Deserialize)] pub struct SteamAccount { pub id: i32, pub steam_username: String, #[serde(default)] pub steam_id: Option, pub is_active: bool, pub created_at: String, #[serde(default)] pub updated_at: Option, } #[derive(Debug, Serialize, Deserialize)] pub struct SteamAccountLimitData { pub max_steam_accounts: i32, pub current_bound_count: i32, } #[derive(Debug, Serialize, Deserialize, Clone)] pub struct Announcement { pub id: i32, pub title: String, pub content: String, #[serde(default = "default_is_active")] pub is_active: bool, #[serde(rename = "publish_time")] pub created_at: String, } fn default_is_active() -> bool { true } #[derive(Debug, Serialize, Deserialize, Clone)] pub struct PublicKeyData { pub public_key: String, #[serde(default)] pub key_version: Option, #[serde(default)] pub signing_public_key: Option, } pub struct BackendApiClient { client: Client, config: ApiConfig, public_key: Option, public_keys: std::collections::HashMap, // 多版本公钥缓存:版本号 -> 公钥 key_order: Vec, // LRU order: oldest at index 0 current_key_version: Option, // 当前使用的密钥版本 session_id: Option, current_user_id: Option, current_username: Option, } impl BackendApiClient { pub fn new(config: ApiConfig) -> Result { // 验证配置 if config.base_url.trim().is_empty() { return Err(config_error("API基础URL不能为空")); } if config.timeout == 0 { return Err(config_error("超时时间不能为0")); } if config.software_code.trim().is_empty() { return Err(config_error("软件特征码不能为空")); } // 复用全局共享的 reqwest Client(带连接池) let client = get_http_client(); Ok(BackendApiClient { client, config, public_key: None, public_keys: std::collections::HashMap::new(), key_order: Vec::new(), current_key_version: None, session_id: None, current_user_id: None, current_username: None, }) } // 设置会话ID pub fn set_session_id(&mut self, session_id: String) { self.session_id = Some(session_id); } #[allow(dead_code)] pub fn set_current_user(&mut self, user_id: Option, username: Option) { self.current_user_id = user_id; self.current_username = username; } // 获取当前会话ID #[allow(dead_code)] pub fn get_session_id(&self) -> Option<&String> { self.session_id.as_ref() } // 清除会话 #[allow(dead_code)] pub fn clear_session(&mut self) { self.session_id = None; self.current_user_id = None; self.current_username = None; } // 获取指定版本的RSA公钥 #[allow(dead_code)] async fn fetch_public_key_by_version(&mut self, key_version: i32) -> Result { // 如果已缓存该版本的公钥,直接返回克隆 if let Some(pk) = self.public_keys.get(&key_version) { return Ok(pk.clone()); } let url = format!("{}/api/v1/keys/version", self.config.base_url); let response = self.client.get(&url).send().await?; let response_text = response.text().await?; // 调试:打印后端原始响应(前 1024 字符)以定位解析错误 { let prefix_len = std::cmp::min(1024, response_text.len()); eprintln!("client response debug (prefix {}): {}", prefix_len, &response_text[..prefix_len]); } #[derive(Deserialize)] struct PublicKeyResponse { code: i32, data: Option, } #[derive(Deserialize)] struct PublicKeyData { current_version: i32, available_versions: Vec, public_key: String, #[serde(default)] signing_public_key: Option, } let api_response: PublicKeyResponse = serde_json::from_str(&response_text)?; if api_response.code == 200 { if let Some(data) = api_response.data { // 后端返回的是 "-----BEGIN PUBLIC KEY-----" (PKCS#8 SubjectPublicKeyInfo) // 先尝试按 PKCS#8 解析,如果失败再回退到 PKCS#1 ("RSA PUBLIC KEY") let public_key = match RsaPublicKey::from_public_key_pem(&data.public_key) { Ok(pk) => pk, Err(_) => { // 回退到 PKCS#1 解析 RsaPublicKey::from_pkcs1_pem(&data.public_key) .map_err(|e| ClientError::RsaError { message: format!("RSA公钥解析失败: {}", e), })? } }; // 缓存该版本的公钥 (LRU cache, keep max 3) self.public_keys.insert(data.current_version, public_key.clone()); // update key_order: move to back self.key_order.retain(|&v| v != data.current_version); self.key_order.push(data.current_version); // evict oldest if over limit if self.key_order.len() > 3 { if let Some(old_v) = self.key_order.first().cloned() { self.public_keys.remove(&old_v); self.key_order.remove(0); } } self.current_key_version = Some(data.current_version); Ok(public_key) } else { Err(ClientError::ApiBusinessError { code: 500, message: "服务器返回数据为空".to_string(), }) } } else { Err(ClientError::ApiBusinessError { code: api_response.code, message: "获取公钥失败".to_string(), }) } } // 获取RSA公钥(兼容性方法,使用当前激活版本) async fn fetch_public_key(&mut self) -> Result { // 如果已缓存公钥,直接返回克隆 if let Some(pk) = &self.public_key { log::info!("[fetch_public_key] 使用缓存公钥 current_key_version={:?}", self.current_key_version); return Ok(pk.clone()); } log::info!("[fetch_public_key] 无缓存,发起 POST /keys/public 握手"); // 服务器期望通过 POST 提交临时 AES 密钥以返回加密的公钥数据 let url = format!("{}/api/v1/keys/public", self.config.base_url); // 1) 生成随机 AES 密钥(32 bytes)并 base64 编码 let mut rng = OsRng; let mut aes_key: [u8; 32] = [0u8; 32]; rng.fill_bytes(&mut aes_key); let aes_key_b64 = general_purpose::STANDARD.encode(&aes_key); eprintln!("[fetch_public_key] aes_key len={} aes_key_b64 len={}", aes_key.len(), aes_key_b64.len()); // 2) 生成 timestamp 和 nonce let timestamp = std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .map_err(|e| ClientError::UnknownError { message: format!("获取时间戳失败: {}", e) })? .as_secs() as i64; let mut nonce_bytes: [u8; 16] = [0u8; 16]; rng.fill_bytes(&mut nonce_bytes); let nonce = nonce_bytes.iter().map(|b| format!("{:02x}", b)).collect::(); // 3) 生成加密的机器特征信息(encrypted_params) // generate_machine_code 当前返回原始特征字符串,例如 "DISK=...;MAC=..." let raw_machine_info = generate_machine_code() .unwrap_or_else(|_| "UNKNOWN_MACHINE".to_string()); let encrypted_params_bytes = encrypt( OpenSSLCipher::aes_256_cbc(), &aes_key, // 这里复用 aes_key 的后16字节作为 IV,与响应解密时的约定保持一致 Some(&aes_key[16..32]), raw_machine_info.as_bytes(), ).map_err(|e| aes_error(format!("加密机器特征信息失败: {}", e)))?; let encrypted_params_b64 = general_purpose::STANDARD.encode(&encrypted_params_bytes); // Retry handshake up to 2 attempts if signature verification fails let mut last_err: Option = None; for attempt in 0..2 { // 4) 构造请求体并发送 POST(明文) let request_body = serde_json::json!({ "encrypted_params": encrypted_params_b64, "aes_key": aes_key_b64, "timestamp": timestamp, "nonce": nonce }); let response = self.client.post(&url) .header("Content-Type", "application/json") .json(&request_body) .send() .await .map_err(|e| network_error(e, format!("发送公钥请求失败: POST {}", url)))?; let response_text = response.text().await .map_err(|e| network_error(e, "读取公钥响应失败"))?; // 调试:打印后端原始响应(前 1024 字符)以定位解析错误 { let prefix_len = std::cmp::min(1024, response_text.len()); eprintln!("client response debug (prefix {}): {}", prefix_len, &response_text[..prefix_len]); } // 解析为通用 ApiResponse 形式 let api_response_value: ApiResponse = serde_json::from_str(&response_text) .map_err(|e| ClientError::SerializationError { source: e, operation: format!("解析公钥响应失败: {}", response_text) })?; if api_response_value.code != 200 { return Err(ClientError::ApiBusinessError { code: api_response_value.code, message: api_response_value.msg, }); } // 取出加密字段 let data_obj = api_response_value.data.ok_or(ClientError::ApiBusinessError { code: 500, message: "公钥响应缺失 data 字段".to_string(), })?; let encrypted_key_b64 = data_obj.get("encrypted_key").and_then(|v| v.as_str()).ok_or(ClientError::ApiBusinessError { code: 500, message: "公钥响应缺失 encrypted_key".to_string(), })?; let encrypted_data_b64 = data_obj.get("encrypted_data").and_then(|v| v.as_str()).ok_or(ClientError::ApiBusinessError { code: 500, message: "公钥响应缺失 encrypted_data".to_string(), })?; // Optional signature verification: server may include signature and signing_public_key (PEM) let signature_opt = data_obj.get("signature").and_then(|v| v.as_str()); let signing_public_key_opt = data_obj.get("signing_public_key").and_then(|v| v.as_str()); let timestamp_opt = data_obj.get("timestamp").and_then(|v| v.as_i64()); let nonce_opt = data_obj.get("nonce").and_then(|v| v.as_str()); // Client-side: 强制要求服务器返回签名与 signing_public_key (阶段性上线) if signature_opt.is_none() || signing_public_key_opt.is_none() { return Err(ClientError::ApiBusinessError { code: 400, message: "服务器未返回签名信息,拒绝响应(强制验签已启用)".to_string() }); } // Verify signature; on failure, retry once by continuing loop if let (Some(sig_b64), Some(signing_pem)) = (signature_opt, signing_public_key_opt) { // Build signature input same as server: "{timestamp}:{nonce}:{base64(encrypted_data)}" let ts = timestamp_opt.unwrap_or(0); let nonce = nonce_opt.unwrap_or(""); let signature_input = format!("{}:{}:{}", ts, nonce, encrypted_data_b64); match PKey::public_key_from_pem(signing_pem.as_bytes()) { Ok(pkey) => { let mut verifier = Verifier::new(MessageDigest::sha256(), &pkey) .map_err(|e| ClientError::UnknownError { message: format!("创建验签器失败: {}", e) })?; verifier.set_rsa_padding(Padding::PKCS1_PSS) .map_err(|e| ClientError::UnknownError { message: format!("设置RSA PSS填充失败: {}", e) })?; verifier.update(signature_input.as_bytes()) .map_err(|e| ClientError::UnknownError { message: format!("验签数据更新失败: {}", e) })?; let sig_bytes = general_purpose::STANDARD.decode(sig_b64) .map_err(|e| ClientError::UnknownError { message: format!("base64 decode signature failed: {}", e) })?; let ok = verifier.verify(&sig_bytes) .map_err(|e| ClientError::UnknownError { message: format!("验签调用失败: {}", e) })?; if !ok { last_err = Some(ClientError::ApiBusinessError { code: 400, message: "服务器签名验证失败".to_string() }); // retry if first attempt if attempt == 0 { continue; } else { return Err(last_err.unwrap()); } } } Err(_) => { // unable to parse signing key, proceed but warn eprintln!("Warning: failed to parse signing_public_key PEM, skipping signature verification"); } } } // if signature verified (or skipped), break loop to continue processing // proceed to decrypt below using encrypted_key_b64 / encrypted_data_b64 let encrypted_key_bytes = general_purpose::STANDARD.decode(encrypted_key_b64) .map_err(|e| ClientError::UnknownError { message: format!("base64 解码 encrypted_key 失败: {}", e) })?; eprintln!( "[fetch_public_key] 准备解密 encrypted_key: encrypted_key_b64_len={} decoded_len={} aes_key_len={} iv_len=16", encrypted_key_b64.len(), encrypted_key_bytes.len(), aes_key.len() ); if encrypted_key_bytes.len() % 16 != 0 { eprintln!("[fetch_public_key] 警告: encrypted_key_bytes 长度不是 16 的倍数,CBC 可能失败"); } // openssl decrypt (key=aes_key[0..32], iv=aes_key[16..32],与后端 key_c=client_aes[:32], iv_c=client_aes[16:32] 一致) let response_key_iv = openssl::symm::decrypt( OpenSSLCipher::aes_256_cbc(), &aes_key, Some(&aes_key[16..32]), &encrypted_key_bytes, ).map_err(|e| { eprintln!( "[fetch_public_key] 解密 encrypted_key 失败: err={:?} encrypted_key_bytes_len={} aes_key_hex_prefix={:02x?}", e, encrypted_key_bytes.len(), &aes_key[..8.min(aes_key.len())] ); aes_error(format!("解密 encrypted_key 失败: {}", e)) })?; if response_key_iv.len() < 48 { return Err(ClientError::ApiBusinessError { code: 500, message: "解密后的 key_iv 长度异常".to_string(), }); } let response_aes_key = &response_key_iv[0..32]; let response_iv = &response_key_iv[32..48]; // 5) 解析 encrypted_data -> decrypted_str (reuse existing code flow) let encrypted_data_bytes = general_purpose::STANDARD.decode(encrypted_data_b64) .map_err(|e| ClientError::UnknownError { message: format!("base64 解码 encrypted_data 失败: {}", e) })?; let decrypted_data_bytes = openssl::symm::decrypt( OpenSSLCipher::aes_256_cbc(), response_aes_key, Some(response_iv), &encrypted_data_bytes, ).map_err(|e| aes_error(format!("解密 encrypted_data 失败: {}", e)))?; let decrypted_str = String::from_utf8(decrypted_data_bytes) .map_err(|e| ClientError::UnknownError { message: format!("UTF8 解码失败: {}", e) })?; #[derive(Deserialize)] struct InnerPublicKey { public_key: String, #[serde(default)] key_version: Option, #[serde(default)] signing_public_key: Option, } let public_key_data: InnerPublicKey = serde_json::from_str(&decrypted_str) .map_err(|e| ClientError::SerializationError { source: e, operation: format!("解析解密后的公钥 JSON 失败: {}", decrypted_str) })?; // 6) 解析 RSA 公钥 let public_key = match RsaPublicKey::from_public_key_pem(&public_key_data.public_key) { Ok(pk) => pk, Err(_) => { RsaPublicKey::from_pkcs1_pem(&public_key_data.public_key) .map_err(|e| ClientError::RsaError { message: format!("RSA公钥解析失败: {}", e) })? } }; // 缓存并记录版本 if let Some(v) = public_key_data.key_version { self.current_key_version = Some(v); } self.public_key = Some(public_key.clone()); // 同步到 key_storage,使 secure_heartbeat / secure_user_login 等使用同一套密钥,避免登出后再登录因密钥版本过期失败 let kv = public_key_data.key_version.unwrap_or(1); crate::key_storage::store_rsa_key_segments(public_key_data.public_key.clone(), kv); return Ok(public_key); } // end retry loop // if here, return last_err or generic error if let Some(err) = last_err { return Err(err); } else { return Err(ClientError::ApiBusinessError { code: 500, message: "未知的握手失败".to_string() }); } } // RSA+AES混合加密 fn encrypt_request_data(&self, data: serde_json::Value, public_key: &RsaPublicKey) -> Result> { // 生成随机AES密钥和IV let mut rng = OsRng; let mut aes_key: [u8; 32] = [0u8; 32]; rng.fill_bytes(&mut aes_key); let mut iv: [u8; 16] = [0u8; 16]; rng.fill_bytes(&mut iv); // JSON序列化 let json_str = serde_json::to_string(&data)?; let plaintext = json_str.as_bytes(); // AES-CBC 加密(使用 OpenSSL,保证与后端兼容的 PKCS7 填充) let ciphertext = encrypt( OpenSSLCipher::aes_256_cbc(), &aes_key, Some(&iv), plaintext, ).map_err(|e| format!("AES encrypt failed: {}", e))?; // 使用RSA加密AES密钥 let padding = Oaep::new::(); let encrypted_aes_key = public_key.encrypt(&mut rng, padding, &aes_key) .map_err(|e| format!("Failed to encrypt AES key: {}", e))?; // 组合:加密的AES密钥(256字节) + IV(16字节) + 加密的数据 let mut result = Vec::new(); result.extend_from_slice(&encrypted_aes_key); result.extend_from_slice(&iv); result.extend_from_slice(&ciphertext); // 发送前调试日志(记录各段长度和 base64 前缀,便于后端对比) { let total_len = result.len(); let enc_key_len = encrypted_aes_key.len(); let iv_len = iv.len(); let payload_len = ciphertext.len(); // 输出到 stderr,便于在控制台/日志中查看 eprintln!("client encrypt debug: total_len={}, enc_key_len={}, iv_len={}, payload_len={}", total_len, enc_key_len, iv_len, payload_len); } // Base64编码 let encoded = general_purpose::STANDARD.encode(result); // 输出 base64 前缀(最多 120 字符),避免泄露全部密文 { let prefix_len = std::cmp::min(120, encoded.len()); eprintln!("client encrypt debug: base64_prefix={}", &encoded[..prefix_len]); } Ok(encoded) } // 解密响应数据 fn decrypt_response_data(&self, encrypted_data: &str) -> Result> { let json_str = general_purpose::STANDARD.decode(encrypted_data)?; let json_str = String::from_utf8(json_str)?; Ok(serde_json::from_str(&json_str)?) } /// 处理带签名的安全响应 /// /// 新的响应格式包含: /// - encrypted_data: AES加密的响应数据 /// - timestamp: 响应时间戳 /// - nonce: 响应随机字符串 /// - signature: HMAC-SHA256签名 /// - key_version: 密钥版本 fn decrypt_secure_response serde::Deserialize<'de>>( &self, response_value: &serde_json::Value, ) -> Result, ClientError> { // 提取签名响应字段 let encrypted_data = match response_value.get("encrypted_data") { Some(serde_json::Value::String(s)) => s.clone(), _ => { // 如果没有 encrypted_data,尝试从 data 字段获取(兼容旧格式) if let Some(data) = response_value.get("data") { if let Some(s) = data.as_str() { return self.decrypt_secure_response_string::(s, response_value); } // 直接解析为 T let result: T = serde_json::from_value(data.clone()) .map_err(|e| serialization_error(e, "解析响应数据"))?; return Ok(ApiResponse { code: response_value.get("code").and_then(|c| c.as_i64()).unwrap_or(200) as i32, msg: response_value.get("msg").and_then(|m| m.as_str()).unwrap_or("成功").to_string(), data: Some(result), }); } return Err(ClientError::UnknownError { message: "响应中缺少 encrypted_data 字段".to_string(), }); } }; // 解密响应数据 let decrypted_value = self.decrypt_response_data(&encrypted_data) .map_err(|e| aes_error(format!("响应数据解密失败: {}", e)))?; let result: T = serde_json::from_value(decrypted_value.clone()) .map_err(|e| serialization_error(e, "解析解密后的数据"))?; // 提取原始响应信息 let code = response_value.get("code").and_then(|c| c.as_i64()).unwrap_or(200) as i32; let msg = response_value.get("msg").and_then(|m| m.as_str()).unwrap_or("成功").to_string(); Ok(ApiResponse { code, msg, data: Some(result), }) } /// 处理带签名的安全响应(字符串格式) fn decrypt_secure_response_string serde::Deserialize<'de>>( &self, encrypted_data: &str, original_response: &serde_json::Value, ) -> Result, ClientError> { let decrypted_value = self.decrypt_response_data(encrypted_data) .map_err(|e| aes_error(format!("响应数据解密失败: {}", e)))?; let result: T = serde_json::from_value(decrypted_value) .map_err(|e| serialization_error(e, "解析解密后的数据"))?; let code = original_response.get("code").and_then(|c| c.as_i64()).unwrap_or(200) as i32; let msg = original_response.get("msg").and_then(|m| m.as_str()).unwrap_or("成功").to_string(); Ok(ApiResponse { code, msg, data: Some(result), }) } async fn make_request Deserialize<'de>>( &mut self, method: reqwest::Method, endpoint: &str, data: Option, ) -> Result, ClientError> { let url = format!("{}{}", self.config.base_url, endpoint); log::info!("[make_request] {} {} (has_cached_pubkey={})", method, endpoint, self.public_key.is_some()); // 获取(或从缓存)公钥 let public_key = self.fetch_public_key().await?; // 准备请求数据 let mut request_data = data.unwrap_or(serde_json::json!({})); request_data["software_code"] = self.config.software_code.clone().into(); // 如果有session_id,添加到请求数据中 if let Some(session_id) = &self.session_id { request_data["session_id"] = session_id.clone().into(); } let encrypted_data = self.encrypt_request_data(request_data, &public_key) .map_err(|e| rsa_error(format!("请求数据加密失败: {}", e)))?; // 获取当前使用的密钥版本 let key_version = self.current_key_version.unwrap_or(1); let request = self.client .request(method.clone(), &url) .header("Content-Type", "application/json") // include session id so backend dependencies can validate before decrypting body .header("X-Session-ID", match &self.session_id { Some(sid) => sid.clone(), None => "".to_string() }) // include user hint headers when available; backend will validate these against session/token .header("X-User-Id", match self.current_user_id { Some(id) => id.to_string(), None => "".to_string() }) .header("X-User-Name", match &self.current_username { Some(name) => name.clone(), None => "".to_string() }) .json(&serde_json::json!({ "encrypted": encrypted_data, "key_version": key_version })); let response = request.send().await .map_err(|e| network_error(e, format!("发送请求失败: {} {}", method, endpoint)))?; let status = response.status(); if !status.is_success() { let error_text = response.text().await.unwrap_or_default(); return Err(ClientError::ServerError { status: status.as_u16(), message: format!("HTTP {}: {}", status, error_text), }); } let response_text = response.text().await .map_err(|e| network_error(e, "读取响应失败"))?; // 先解析为通用 Value 形式,兼容两种后端返回: // 1) ApiResponse.data 是加密后的字符串(String) // 2) ApiResponse.data 是直接的 JSON 对象(map) let api_response_value: ApiResponse = serde_json::from_str(&response_text) .map_err(|e| serialization_error(e, "解析API响应"))?; if api_response_value.code != 200 { return Err(ClientError::ApiBusinessError { code: api_response_value.code, message: api_response_value.msg, }); } if let Some(raw_data) = api_response_value.data { match raw_data { // 情况 A:后端返回加密的字符串(base64),需要解密后反序列化为 T serde_json::Value::String(encrypted_str) => { let decrypted_value = self.decrypt_response_data(&encrypted_str) .map_err(|e| aes_error(format!("响应数据解密失败: {}", e)))?; let result: T = serde_json::from_value(decrypted_value) .map_err(|e| serialization_error(e, "解析解密后的数据"))?; Ok(ApiResponse { code: api_response_value.code, msg: api_response_value.msg, data: Some(result), }) } // 情况 B:后端直接返回 JSON 对象,直接转换为 T other => { let result: T = serde_json::from_value(other) .map_err(|e| serialization_error(e, "解析未加密响应数据"))?; Ok(ApiResponse { code: api_response_value.code, msg: api_response_value.msg, data: Some(result), }) } } } else { Ok(ApiResponse { code: api_response_value.code, msg: api_response_value.msg, data: None, }) } } // 用户相关API pub async fn register(&mut self, username: &str, password: &str, email: &str, card_code: &str, machine_code: &str) -> Result, ClientError> { let data = serde_json::json!({ "username": username, "password": password, "email": email, "card_code": card_code, "machine_code": machine_code }); self.make_plaintext_request(reqwest::Method::POST, "/api/user/register", Some(data)).await } pub async fn login(&mut self, username: &str, password: &str, machine_code: &str) -> Result, ClientError> { let data = serde_json::json!({ "username": username, "password": password, "machine_code": machine_code }); let result: ApiResponse = self.make_plaintext_request(reqwest::Method::POST, "/api/user/login", Some(data)).await?; // 如果登录成功,设置session_id if result.code == 200 { if let Some(user_info) = &result.data { if let Some(session_id) = &user_info.session_id { self.set_session_id(session_id.clone()); } // set current user id / username for header propagation if let Some(uid) = user_info.id { self.current_user_id = Some(uid); } self.current_username = Some(user_info.username.clone()); } } Ok(result) } pub async fn unbind(&mut self, username: &str, password: &str) -> Result, ClientError> { let data = serde_json::json!({ "username": username, "password": password }); self.make_plaintext_request(reqwest::Method::POST, "/api/user/unbind", Some(data)).await } pub async fn recharge(&mut self, username: &str, password: &str, card_code: &str) -> Result, ClientError> { let data = serde_json::json!({ "username": username, "password": password, "cardCode": card_code }); self.make_plaintext_request(reqwest::Method::POST, "/api/user/recharge", Some(data)).await } pub async fn heartbeat(&mut self, user_id: i32) -> Result, ClientError> { let data = serde_json::json!({ "user_id": user_id }); self.make_request(reqwest::Method::POST, "/api/user/heartbeat", Some(data)).await } /// 登出:加密请求(make_request),与其它接口一致;依赖公钥缓存或握手。 pub async fn logout(&mut self, user_id: i32, session_id: &str) -> Result, ClientError> { log::info!("[ApiClient] logout: user_id={} session_id_len={}", user_id, session_id.len()); let data = serde_json::json!({ "user_id": user_id, "session_id": session_id }); self.make_request(reqwest::Method::POST, "/api/v1/auth/logout", Some(data)).await } #[allow(dead_code)] pub async fn validate_session(&mut self, session_id: &str) -> Result, ClientError> { let data = serde_json::json!({ "session_id": session_id }); self.make_request(reqwest::Method::POST, "/api/user/validate-session", Some(data)).await } pub async fn activate_card(&mut self, user_id: i32, card_code: &str) -> Result, ClientError> { let data = serde_json::json!({ "user_id": user_id, "card_code": card_code }); self.make_request(reqwest::Method::POST, "/api/user/activate-card", Some(data)).await } // Steam账号相关API pub async fn get_steam_account_limit(&mut self, user_id: i32) -> Result, ClientError> { let data = serde_json::json!({ "user_id": user_id }); self.make_request(reqwest::Method::POST, "/api/v1/steam/account-limit", Some(data)).await } pub async fn get_steam_accounts(&mut self, user_id: i32) -> Result>, ClientError> { let data = serde_json::json!({ "user_id": user_id }); self.make_request(reqwest::Method::POST, "/api/v1/steam/accounts/list", Some(data)).await } pub async fn create_steam_account(&mut self, user_id: i32, steam_username: &str, steam_id: Option<&str>) -> Result, ClientError> { let data = serde_json::json!({ "user_id": user_id, "steam_username": steam_username, "steam_id": steam_id }); self.make_request(reqwest::Method::POST, "/api/v1/steam/accounts", Some(data)).await } pub async fn bind_steam_account(&mut self, user_id: i32, steam_username: &str, steam_id: &str) -> Result, ClientError> { let data = serde_json::json!({ "user_id": user_id, "steam_username": steam_username, "steam_id": steam_id }); self.make_request(reqwest::Method::POST, "/api/v1/steam/accounts/bind", Some(data)).await } pub async fn update_steam_account(&mut self, user_id: i32, account_id: i32, updates: serde_json::Value) -> Result, ClientError> { let mut data = updates; data["user_id"] = user_id.into(); self.make_request(reqwest::Method::PUT, &format!("/api/v1/steam/accounts/{}", account_id), Some(data)).await } pub async fn delete_steam_account(&mut self, user_id: i32, account_id: i32) -> Result, ClientError> { let data = serde_json::json!({ "user_id": user_id, "account_id": account_id }); self.make_request(reqwest::Method::DELETE, "/api/v1/steam/accounts", Some(data)).await } // 公告相关API pub async fn get_announcements(&mut self, page: i32, page_size: i32, keyword: Option<&str>) -> Result>, ClientError> { let mut params = HashMap::new(); params.insert("page".to_string(), page.to_string()); params.insert("page_size".to_string(), page_size.to_string()); if let Some(kw) = keyword { params.insert("keyword".to_string(), kw.to_string()); } let query_string = params.iter() .map(|(k, v)| format!("{}={}", k, v)) .collect::>() .join("&"); let endpoint = format!("/api/v1/announcements?{}", query_string); // 公告接口不加密,直接调用 let url = format!("{}{}", self.config.base_url, endpoint); let response = self.client.get(&url).send().await?; let response_text = response.text().await?; let api_response: ApiResponse> = serde_json::from_str(&response_text)?; Ok(api_response) } // 健康检查API pub async fn health_check(&self) -> Result, ClientError> { let url = format!("{}/api/v1/health", self.config.base_url); let response = self.client.get(&url).send().await?; let response_text = response.text().await?; let api_response: ApiResponse = serde_json::from_str(&response_text)?; Ok(api_response) } // 明文请求方法(用于不需要加密的接口) async fn make_plaintext_request Deserialize<'de>>( &self, method: reqwest::Method, endpoint: &str, data: Option, ) -> Result, ClientError> { let url = format!("{}{}", self.config.base_url, endpoint); let mut request = self.client .request(method.clone(), &url) .header("Content-Type", "application/json"); // 如果有数据,添加到请求体中 if let Some(request_data) = data { request = request.json(&request_data); } let response = request.send().await .map_err(|e| network_error(e, format!("发送明文请求失败: {} {}", method, endpoint)))?; let status = response.status(); if !status.is_success() { let error_text = response.text().await.unwrap_or_default(); return Err(ClientError::ServerError { status: status.as_u16(), message: format!("HTTP {}: {}", status, error_text), }); } let response_text = response.text().await .map_err(|e| network_error(e, "读取明文响应失败"))?; let api_response: ApiResponse = serde_json::from_str(&response_text) .map_err(|e| ClientError::SerializationError { source: e, operation: format!("解析明文响应失败: {}", response_text) })?; Ok(api_response) } // 公钥相关API pub async fn get_public_key(&self) -> Result, ClientError> { let url = format!("{}/api/v1/keys/public", self.config.base_url); let response = self.client.get(&url).send().await?; let response_text = response.text().await?; let api_response: ApiResponse = serde_json::from_str(&response_text)?; Ok(api_response) } pub async fn get_client_configs(&mut self) -> Result, ClientError> { use base64::{Engine as _, engine::general_purpose}; use openssl::symm::decrypt as openssl_decrypt; use openssl::symm::Cipher as OpenSSLCipher; use std::time::{SystemTime, UNIX_EPOCH}; // 1) Generate a random AES key (32 bytes) and build handshake request let mut rng = OsRng; let mut aes_key: [u8; 32] = [0u8; 32]; rng.fill_bytes(&mut aes_key); let aes_key_b64 = general_purpose::STANDARD.encode(&aes_key); let timestamp = SystemTime::now() .duration_since(UNIX_EPOCH) .map_err(|e| ClientError::UnknownError { message: format!("获取时间戳失败: {}", e) })? .as_secs() as i64; let mut nonce_bytes: [u8; 16] = [0u8; 16]; rng.fill_bytes(&mut nonce_bytes); let nonce = nonce_bytes.iter().map(|b| format!("{:02x}", b)).collect::(); let request_body = serde_json::json!({ "aes_key": aes_key_b64, "timestamp": timestamp, "nonce": nonce }); // Retry handshake up to 2 attempts on signature verification failure let mut last_err_opt: Option = None; for attempt in 0..2 { let api_resp: ApiResponse = self.make_plaintext_request(reqwest::Method::POST, "/api/configs", Some(request_body.clone())).await?; if api_resp.code != 200 { return Ok(ApiResponse { code: api_resp.code, msg: api_resp.msg, data: api_resp.data, }); } let data_obj = api_resp.data.ok_or(ClientError::SerializationError { source: serde_json::Error::io(std::io::Error::new(std::io::ErrorKind::Other, "Missing data field in configs response")), operation: "parse configs response".to_string(), })?; // Extract fields let encrypted_key_b64 = data_obj.get("encrypted_key").and_then(|v| v.as_str()).ok_or(ClientError::SerializationError { source: serde_json::Error::io(std::io::Error::new(std::io::ErrorKind::Other, "missing encrypted_key")), operation: "configs response".to_string(), })?; let encrypted_data_b64 = data_obj.get("encrypted_data").and_then(|v| v.as_str()).ok_or(ClientError::SerializationError { source: serde_json::Error::io(std::io::Error::new(std::io::ErrorKind::Other, "missing encrypted_data")), operation: "configs response".to_string(), })?; // signature fields let signature_opt = data_obj.get("signature").and_then(|v| v.as_str()); let signing_public_key_opt = data_obj.get("signing_public_key").and_then(|v| v.as_str()); let timestamp_opt = data_obj.get("timestamp").and_then(|v| v.as_i64()); let nonce_opt = data_obj.get("nonce").and_then(|v| v.as_str()); if signature_opt.is_none() || signing_public_key_opt.is_none() { return Ok(ApiResponse { code: 400, msg: "服务器未返回签名信息,握手失败".to_string(), data: None }); } let sig_b64 = signature_opt.unwrap(); let signing_pem = signing_public_key_opt.unwrap(); let ts = timestamp_opt.unwrap_or(0); let nonce_s = nonce_opt.unwrap_or(""); let signature_input = format!("{}:{}:{}", ts, nonce_s, encrypted_data_b64); // verify signature let verify_ok = match PKey::public_key_from_pem(signing_pem.as_bytes()) { Ok(pkey) => { let mut verifier = Verifier::new(MessageDigest::sha256(), &pkey) .map_err(|e| ClientError::UnknownError { message: format!("创建验签器失败: {}", e) }).ok(); if verifier.is_none() { false } else { let mut verifier = verifier.unwrap(); if verifier.set_rsa_padding(Padding::PKCS1_PSS).is_err() { false } else { if verifier.update(signature_input.as_bytes()).is_err() { false } else { let sig_bytes = general_purpose::STANDARD.decode(sig_b64).map_err(|e| ClientError::UnknownError { message: format!("base64 decode signature failed: {}", e) }).ok(); if sig_bytes.is_none() { false } else { verifier.verify(&sig_bytes.unwrap()).map_err(|e| ClientError::UnknownError { message: format!("验签调用失败: {}", e) }).unwrap_or(false) } } } } } Err(_) => { eprintln!("Warning: failed to parse signing_public_key PEM, skipping signature verification"); true } }; if !verify_ok { last_err_opt = Some(ClientError::ApiBusinessError { code: 400, message: "服务器签名验证失败".to_string() }); if attempt == 0 { // retry once continue; } else { return Err(last_err_opt.unwrap()); } } // proceed to decrypt as before let encrypted_key_bytes = general_purpose::STANDARD.decode(encrypted_key_b64) .map_err(|e| ClientError::UnknownError { message: format!("base64 decode failed: {}", e) })?; let response_key_iv = openssl_decrypt( OpenSSLCipher::aes_256_cbc(), &aes_key, Some(&aes_key[16..32]), &encrypted_key_bytes, ).map_err(|e| ClientError::UnknownError { message: format!("decrypt encrypted_key failed: {}", e) })?; if response_key_iv.len() < 48 { return Err(ClientError::UnknownError { message: "decrypted key_iv length invalid".to_string() }); } let response_aes_key = &response_key_iv[0..32]; let response_iv = &response_key_iv[32..48]; // 4) Decrypt encrypted_data using response_aes_key and response_iv (AES-CBC) let encrypted_data_bytes = general_purpose::STANDARD.decode(encrypted_data_b64) .map_err(|e| ClientError::UnknownError { message: format!("base64 decode failed: {}", e) })?; let decrypted_bytes = openssl_decrypt( OpenSSLCipher::aes_256_cbc(), response_aes_key, Some(response_iv), &encrypted_data_bytes, ).map_err(|e| ClientError::UnknownError { message: format!("decrypt encrypted_data failed: {}", e) })?; // Parse decrypted JSON payload let decrypted_value: serde_json::Value = serde_json::from_slice(&decrypted_bytes) .map_err(|e| ClientError::SerializationError { source: e, operation: "parse decrypted configs".to_string() })?; return Ok(ApiResponse { code: api_resp.code, msg: api_resp.msg, data: Some(decrypted_value), }); } // no successful attempt if let Some(err) = last_err_opt { Err(err) } else { Err(ClientError::ApiBusinessError { code: 500, message: "未知的握手失败".to_string() }) } } } // 全局API客户端实例 lazy_static::lazy_static! { static ref API_CLIENT: std::sync::Mutex> = std::sync::Mutex::new(None); } // 全局共享的 reqwest Client(带连接池,减少 TCP 连接开销) lazy_static::lazy_static! { static ref HTTP_CLIENT: reqwest::Client = reqwest::Client::builder() .pool_max_idle_per_host(16) // 每个 host 最多保留 16 个空闲连接 .pool_idle_timeout(std::time::Duration::from_secs(30)) // 空闲连接 30s 后释放 .tcp_keepalive(std::time::Duration::from_secs(60)) // TCP keep-alive 60s .build() .expect("创建全局 HTTP 客户端失败"); } /// 获取全局共享的 reqwest Client(带连接池) pub fn get_http_client() -> reqwest::Client { HTTP_CLIENT.clone() } pub fn get_api_client() -> Result>, Box> { Ok(API_CLIENT.lock().map_err(|e| format!("Failed to lock API client: {}", e))?) } pub fn initialize_api_client(config: ApiConfig) -> Result<(), ClientError> { let client = BackendApiClient::new(config)?; let mut guard = get_api_client().map_err(|e| ClientError::UnknownError { message: format!("获取API客户端锁失败: {}", e), })?; *guard = Some(client); Ok(()) } // 获取API客户端的克隆版本(用于async函数) pub fn get_api_client_for_async() -> Result { let guard = get_api_client().map_err(|e| ClientError::UnknownError { message: format!("获取API客户端锁失败: {}", e), })?; if let Some(client) = guard.as_ref() { // 注意:这里我们克隆客户端,这不是最优的解决方案 // 在生产环境中,应该使用Arc>或其他并发安全的解决方案 Ok(BackendApiClient { client: client.client.clone(), config: client.config.clone(), public_key: client.public_key.clone(), public_keys: client.public_keys.clone(), key_order: client.key_order.clone(), current_key_version: client.current_key_version.clone(), session_id: client.session_id.clone(), current_user_id: client.current_user_id.clone(), current_username: client.current_username.clone(), }) } else { Err(ClientError::ClientNotInitialized) } }