| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162 |
- 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<T> {
- pub code: i32,
- pub msg: String,
- pub data: Option<T>,
- }
- #[derive(Debug, Serialize, Deserialize)]
- pub struct UserInfo {
- #[serde(rename = "user_id")]
- pub id: Option<i32>,
- pub username: String,
- #[serde(default)]
- pub email: Option<String>,
- #[serde(default)]
- pub expire_time: Option<String>,
- #[serde(default)]
- pub is_active: Option<bool>,
- #[serde(default)]
- pub session_id: Option<String>,
- }
- #[derive(Debug, Serialize, Deserialize)]
- pub struct SteamAccount {
- pub id: i32,
- pub steam_username: String,
- #[serde(default)]
- pub steam_id: Option<String>,
- pub is_active: bool,
- pub created_at: String,
- #[serde(default)]
- pub updated_at: Option<String>,
- }
- #[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<i32>,
- #[serde(default)]
- pub signing_public_key: Option<String>,
- }
- pub struct BackendApiClient {
- client: Client,
- config: ApiConfig,
- public_key: Option<RsaPublicKey>,
- public_keys: std::collections::HashMap<i32, RsaPublicKey>, // 多版本公钥缓存:版本号 -> 公钥
- key_order: Vec<i32>, // LRU order: oldest at index 0
- current_key_version: Option<i32>, // 当前使用的密钥版本
- session_id: Option<String>,
- current_user_id: Option<i32>,
- current_username: Option<String>,
- }
- impl BackendApiClient {
- pub fn new(config: ApiConfig) -> Result<Self, ClientError> {
- // 验证配置
- 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<i32>, username: Option<String>) {
- 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<RsaPublicKey, ClientError> {
- // 如果已缓存该版本的公钥,直接返回克隆
- 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<PublicKeyData>,
- }
- #[derive(Deserialize)]
- struct PublicKeyData {
- current_version: i32,
- available_versions: Vec<i32>,
- public_key: String,
- #[serde(default)]
- signing_public_key: Option<String>,
- }
- 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<RsaPublicKey, ClientError> {
- // 如果已缓存公钥,直接返回克隆
- 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::<String>();
- // 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<ClientError> = 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::Value> = 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<i32>,
- #[serde(default)]
- signing_public_key: Option<String>,
- }
- 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<String, Box<dyn std::error::Error>> {
- // 生成随机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::<Sha256>();
- 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<serde_json::Value, Box<dyn std::error::Error>> {
- 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<T: for<'de> serde::Deserialize<'de>>(
- &self,
- response_value: &serde_json::Value,
- ) -> Result<ApiResponse<T>, 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::<T>(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<T: for<'de> serde::Deserialize<'de>>(
- &self,
- encrypted_data: &str,
- original_response: &serde_json::Value,
- ) -> Result<ApiResponse<T>, 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<T: for<'de> Deserialize<'de>>(
- &mut self,
- method: reqwest::Method,
- endpoint: &str,
- data: Option<serde_json::Value>,
- ) -> Result<ApiResponse<T>, 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::Value> = 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<ApiResponse<UserInfo>, 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<ApiResponse<UserInfo>, ClientError> {
- let data = serde_json::json!({
- "username": username,
- "password": password,
- "machine_code": machine_code
- });
- let result: ApiResponse<UserInfo> = 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<ApiResponse<serde_json::Value>, 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<ApiResponse<serde_json::Value>, 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<ApiResponse<serde_json::Value>, 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<ApiResponse<serde_json::Value>, 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<ApiResponse<serde_json::Value>, 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<ApiResponse<serde_json::Value>, 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<ApiResponse<SteamAccountLimitData>, 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<ApiResponse<Vec<SteamAccount>>, 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<ApiResponse<SteamAccount>, 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<ApiResponse<SteamAccount>, 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<ApiResponse<SteamAccount>, 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<ApiResponse<serde_json::Value>, 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<ApiResponse<Vec<Announcement>>, 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::<Vec<_>>()
- .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<Vec<Announcement>> = serde_json::from_str(&response_text)?;
- Ok(api_response)
- }
- // 健康检查API
- pub async fn health_check(&self) -> Result<ApiResponse<serde_json::Value>, 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::Value> = serde_json::from_str(&response_text)?;
- Ok(api_response)
- }
- // 明文请求方法(用于不需要加密的接口)
- async fn make_plaintext_request<T: for<'de> Deserialize<'de>>(
- &self,
- method: reqwest::Method,
- endpoint: &str,
- data: Option<serde_json::Value>,
- ) -> Result<ApiResponse<T>, 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<T> = 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<ApiResponse<PublicKeyData>, 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<PublicKeyData> = serde_json::from_str(&response_text)?;
- Ok(api_response)
- }
- pub async fn get_client_configs(&mut self) -> Result<ApiResponse<serde_json::Value>, 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::<String>();
- 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<ClientError> = None;
- for attempt in 0..2 {
- let api_resp: ApiResponse<serde_json::Value> = 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<Option<BackendApiClient>> = 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<std::sync::MutexGuard<'static, Option<BackendApiClient>>, Box<dyn std::error::Error>> {
- 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<BackendApiClient, ClientError> {
- let guard = get_api_client().map_err(|e| ClientError::UnknownError {
- message: format!("获取API客户端锁失败: {}", e),
- })?;
- if let Some(client) = guard.as_ref() {
- // 注意:这里我们克隆客户端,这不是最优的解决方案
- // 在生产环境中,应该使用Arc<Mutex<>>或其他并发安全的解决方案
- 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)
- }
- }
|