| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 |
- """
- 客户端配置接口(客户端使用)
- 已调整为 POST,并使用与 /api/keys/public 相同的握手加密格式返回:
- 返回结构为 {"code":200,"msg":"成功","data":{"encrypted_key": "...", "encrypted_data": "...", "signature": "...", "timestamp": ..., "nonce": "..." }}
- 客户端需在请求中提供 Base64 的 aes_key(32 bytes)、timestamp、nonce。
- """
- from fastapi import APIRouter, Depends
- from pydantic import BaseModel
- from sqlalchemy.orm import Session
- import json
- import base64
- import secrets
- from app.database import get_db
- from app.models.system_config import SystemConfig
- from app.utils.timestamp_validator import TimestampValidator
- from app.config import settings
- # reuse AES helper and signature generator from keys module
- from app.api.client.keys import _aes_encrypt, _generate_signature
- router = APIRouter()
- class ConfigsRequest(BaseModel):
- aes_key: str
- timestamp: int
- nonce: str
- @router.post("")
- async def get_client_configs(request: ConfigsRequest, db: Session = Depends(get_db)):
- """获取客户端配置(供客户端使用),使用 keys/public 相同的加密握手协议"""
- try:
- # 1. 验证时间戳
- is_valid, err_msg = TimestampValidator.validate_timestamp(request.timestamp, db)
- if not is_valid:
- return {
- "code": 400,
- "msg": f"时间戳验证失败: {err_msg}",
- "timestamp": TimestampValidator.generate_timestamp()
- }
- # 2. 验证并解码客户端提供的 AES 密钥
- try:
- client_aes_key = base64.b64decode(request.aes_key)
- if len(client_aes_key) != 32:
- raise ValueError("AES密钥长度无效")
- except Exception as e:
- return {
- "code": 400,
- "msg": "无效的AES密钥格式",
- "timestamp": TimestampValidator.generate_timestamp()
- }
- # 3. 准备要返回的客户端配置数据
- client_configs = {
- "api_timeout_seconds": SystemConfig.get_config_value('api_timeout_seconds', 30),
- "client_heartbeat_interval_seconds": SystemConfig.get_config_value('client_heartbeat_interval_seconds', 30),
- "max_heartbeat_failures": SystemConfig.get_config_value('max_heartbeat_failures', 10),
- "max_network_failures": SystemConfig.get_config_value('max_network_failures', 15),
- }
- # 4. 使用随机 response AES 密钥/IV 加密实际 payload
- response_aes_key = secrets.token_bytes(32)
- response_iv = secrets.token_bytes(16)
- data_json = json.dumps(client_configs, ensure_ascii=False)
- encrypted_data = _aes_encrypt(data_json.encode('utf-8'), response_aes_key, response_iv)
- # 5. 使用客户端提供的 AES 密钥加密 response_aes_key + iv(便于客户端解密)
- key_iv_combined = response_aes_key + response_iv
- encrypted_key_iv = _aes_encrypt(key_iv_combined, client_aes_key, client_aes_key[16:32])
- # 6. 生成签名(timestamp:nonce:base64(encrypted_data))
- timestamp = TimestampValidator.generate_timestamp()
- signature_data = f"{timestamp}:{request.nonce}:{base64.b64encode(encrypted_data).decode()}"
- signature = _generate_signature(signature_data, settings.SECRET_KEY)
- # 7. 返回与 /api/keys/public 相同的格式
- response = {
- "code": 200,
- "msg": "成功",
- "data": {
- "encrypted_key": base64.b64encode(encrypted_key_iv).decode(),
- "encrypted_data": base64.b64encode(encrypted_data).decode(),
- "signature": signature,
- "timestamp": timestamp,
- "nonce": request.nonce
- }
- }
- return response
- except Exception as e:
- return {
- "code": 500,
- "msg": "系统异常,请稍后重试;若持续发生请联系客服",
- "timestamp": TimestampValidator.generate_timestamp()
- }
|