mdbq 4.0.123__py3-none-any.whl → 4.0.125__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of mdbq might be problematic. Click here for more details.
- mdbq/__version__.py +1 -1
- mdbq/auth/crypto.py +296 -596
- {mdbq-4.0.123.dist-info → mdbq-4.0.125.dist-info}/METADATA +1 -1
- {mdbq-4.0.123.dist-info → mdbq-4.0.125.dist-info}/RECORD +6 -6
- {mdbq-4.0.123.dist-info → mdbq-4.0.125.dist-info}/WHEEL +0 -0
- {mdbq-4.0.123.dist-info → mdbq-4.0.125.dist-info}/top_level.txt +0 -0
mdbq/__version__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
VERSION = '4.0.
|
|
1
|
+
VERSION = '4.0.125'
|
mdbq/auth/crypto.py
CHANGED
|
@@ -1,26 +1,6 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
"""
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
功能特性:
|
|
6
|
-
- RSA-OAEP + AES-GCM 混合加密
|
|
7
|
-
- dpflask风格设备指纹验证
|
|
8
|
-
- 时间窗口验证
|
|
9
|
-
- PEM格式密钥处理
|
|
10
|
-
- Redis防重放攻击(可选)
|
|
11
|
-
|
|
12
|
-
使用场景:
|
|
13
|
-
- 敏感数据传输加密
|
|
14
|
-
- 用户认证系统
|
|
15
|
-
- API安全通信
|
|
16
|
-
- 设备身份验证
|
|
17
|
-
|
|
18
|
-
依赖说明:
|
|
19
|
-
- cryptography: RSA和AES加密算法
|
|
20
|
-
- hashlib: 哈希计算(Python标准库)
|
|
21
|
-
- json: JSON处理(Python标准库)
|
|
22
|
-
- base64: Base64编码(Python标准库)
|
|
23
|
-
- redis: 防重放攻击(可选)
|
|
3
|
+
加密解密管理模块
|
|
24
4
|
"""
|
|
25
5
|
|
|
26
6
|
import os
|
|
@@ -30,11 +10,10 @@ import time
|
|
|
30
10
|
import hashlib
|
|
31
11
|
import hmac
|
|
32
12
|
import threading
|
|
13
|
+
from abc import ABC, abstractmethod
|
|
33
14
|
from dataclasses import dataclass, field
|
|
34
15
|
from enum import Enum
|
|
35
|
-
from typing import Dict, Any, Optional, Union, List, Protocol, runtime_checkable
|
|
36
|
-
|
|
37
|
-
# 加密相关导入
|
|
16
|
+
from typing import Dict, Any, Optional, Union, List, Protocol, runtime_checkable, Tuple
|
|
38
17
|
from cryptography.hazmat.primitives import serialization, hashes
|
|
39
18
|
from cryptography.hazmat.primitives.asymmetric import padding
|
|
40
19
|
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
|
@@ -42,697 +21,418 @@ from cryptography.hazmat.backends import default_backend
|
|
|
42
21
|
from mdbq.myconf import myconf # type: ignore
|
|
43
22
|
from mdbq.log import mylogger
|
|
44
23
|
|
|
45
|
-
dir_path = os.path.expanduser("~")
|
|
46
|
-
config_file = os.path.join(dir_path, 'spd.txt')
|
|
47
|
-
parser = myconf.ConfigParser()
|
|
48
|
-
logger = mylogger.MyLogger(
|
|
49
|
-
logging_mode='file',
|
|
50
|
-
log_level='info',
|
|
51
|
-
log_format='json',
|
|
52
|
-
max_log_size=50,
|
|
53
|
-
backup_count=5,
|
|
54
|
-
enable_async=False, # 是否启用异步日志
|
|
55
|
-
sample_rate=1, # 采样DEBUG/INFO日志
|
|
56
|
-
sensitive_fields=[], # 敏感字段过滤
|
|
57
|
-
enable_metrics=False, # 是否启用性能指标
|
|
58
|
-
)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
# ==================== 枚举和常量定义 ====================
|
|
62
|
-
|
|
63
|
-
class ValidationResult(Enum):
|
|
64
|
-
"""验证结果枚举"""
|
|
65
|
-
SUCCESS = "success"
|
|
66
|
-
DECRYPT_FAILED = "decrypt_failed"
|
|
67
|
-
TIMESTAMP_INVALID = "timestamp_invalid"
|
|
68
|
-
FINGERPRINT_MISMATCH = "fingerprint_mismatch"
|
|
69
|
-
NONCE_REUSED = "nonce_reused"
|
|
70
|
-
INVALID_PAYLOAD = "invalid_payload"
|
|
71
|
-
KEY_LOAD_FAILED = "key_load_failed"
|
|
72
|
-
REDIS_ERROR = "redis_error"
|
|
73
24
|
|
|
74
|
-
# ====================
|
|
25
|
+
# ==================== 配置和常量 ====================
|
|
75
26
|
|
|
76
27
|
@dataclass
|
|
77
28
|
class CryptoConfig:
|
|
78
|
-
"""
|
|
29
|
+
"""
|
|
30
|
+
加密配置类
|
|
31
|
+
"""
|
|
79
32
|
# 密钥配置
|
|
80
|
-
key_dir_path: str =
|
|
33
|
+
key_dir_path: str = os.path.expanduser("~")
|
|
34
|
+
keys_subdir: str = 'dpsk_keys'
|
|
81
35
|
public_key_filename: str = 'public_key'
|
|
82
36
|
private_key_filename: str = 'private_key'
|
|
83
|
-
keys_subdir: str = 'dpsk_keys'
|
|
84
|
-
|
|
85
|
-
# 验证配置
|
|
86
|
-
time_window_seconds: int = 300
|
|
87
|
-
enable_nonce_check: bool = True
|
|
88
|
-
nonce_expire_seconds: int = 600
|
|
89
|
-
nonce_redis_prefix: str = "api接口身份唯一性校检"
|
|
90
37
|
|
|
91
38
|
# 缓存配置
|
|
92
39
|
enable_key_cache: bool = True
|
|
93
40
|
key_cache_ttl_seconds: int = 3600
|
|
94
41
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
raise ValueError("nonce_expire_seconds must be positive")
|
|
42
|
+
# 验证配置
|
|
43
|
+
time_window_seconds: int = 300
|
|
44
|
+
enable_nonce_check: bool = False
|
|
45
|
+
nonce_expire_seconds: int = 600
|
|
46
|
+
nonce_redis_prefix: str = "crypto_nonce"
|
|
101
47
|
|
|
102
48
|
|
|
103
49
|
# ==================== 结果类 ====================
|
|
104
50
|
|
|
105
51
|
@dataclass
|
|
106
|
-
class
|
|
107
|
-
"""
|
|
52
|
+
class AuthenticationResult:
|
|
53
|
+
"""认证结果"""
|
|
108
54
|
success: bool
|
|
109
|
-
result_code: ValidationResult
|
|
110
55
|
payload: Optional[Dict[str, Any]] = None
|
|
111
|
-
|
|
112
|
-
execution_time_ms:
|
|
113
|
-
debug_info: Optional[Dict[str, Any]] = None
|
|
56
|
+
error_message: Optional[str] = None
|
|
57
|
+
execution_time_ms: float = 0.0
|
|
114
58
|
|
|
115
59
|
@classmethod
|
|
116
|
-
def success_result(cls, payload: Dict[str, Any],
|
|
117
|
-
|
|
118
|
-
return cls(
|
|
119
|
-
success=True,
|
|
120
|
-
result_code=ValidationResult.SUCCESS,
|
|
121
|
-
payload=payload,
|
|
122
|
-
execution_time_ms=execution_time_ms
|
|
123
|
-
)
|
|
60
|
+
def success_result(cls, payload: Dict[str, Any], execution_time: float = 0.0):
|
|
61
|
+
return cls(success=True, payload=payload, execution_time_ms=execution_time)
|
|
124
62
|
|
|
125
63
|
@classmethod
|
|
126
|
-
def failure_result(cls,
|
|
127
|
-
|
|
128
|
-
"""创建失败结果"""
|
|
129
|
-
return cls(
|
|
130
|
-
success=False,
|
|
131
|
-
result_code=result_code,
|
|
132
|
-
error_details=error_details,
|
|
133
|
-
execution_time_ms=execution_time_ms,
|
|
134
|
-
debug_info=debug_info
|
|
135
|
-
)
|
|
64
|
+
def failure_result(cls, error_message: str, execution_time: float = 0.0):
|
|
65
|
+
return cls(success=False, error_message=error_message, execution_time_ms=execution_time)
|
|
136
66
|
|
|
137
67
|
|
|
138
|
-
# ====================
|
|
68
|
+
# ==================== 密钥管理 ====================
|
|
139
69
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
"""设备指纹验证器接口"""
|
|
70
|
+
class KeyManager:
|
|
71
|
+
"""密钥管理器 - 单例模式"""
|
|
143
72
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
class DpflaskFingerprintValidator:
|
|
150
|
-
"""dpflask风格设备指纹验证器"""
|
|
73
|
+
_instance = None
|
|
74
|
+
_lock = threading.Lock()
|
|
75
|
+
_cache = {}
|
|
76
|
+
_cache_time = {}
|
|
151
77
|
|
|
152
|
-
def
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
if not client_hash:
|
|
159
|
-
if logger_func:
|
|
160
|
-
logger_func("设备指纹哈希为空")
|
|
161
|
-
return False
|
|
162
|
-
|
|
163
|
-
def deep_sort(obj):
|
|
164
|
-
if isinstance(obj, dict):
|
|
165
|
-
return {k: deep_sort(v) for k, v in sorted(obj.items(), key=lambda x: x)}
|
|
166
|
-
if isinstance(obj, list):
|
|
167
|
-
return sorted((deep_sort(x) for x in obj),
|
|
168
|
-
key=lambda x: (isinstance(x, (int, float)), x))
|
|
169
|
-
return obj
|
|
170
|
-
|
|
171
|
-
# 严格类型转换保证与前端一致
|
|
172
|
-
standardized_data = deep_sort({
|
|
173
|
-
"userAgent": str(client_data.get("userAgent", "")),
|
|
174
|
-
"platform": str(client_data.get("platform", "")),
|
|
175
|
-
"languages": sorted([str(x) for x in client_data.get("languages", [])]),
|
|
176
|
-
"hardwareConcurrency": int(client_data.get("hardwareConcurrency", 0)),
|
|
177
|
-
"screenProps": {
|
|
178
|
-
"width": int(client_data.get("screenProps", {}).get("width", 0)),
|
|
179
|
-
"height": int(client_data.get("screenProps", {}).get("height", 0)),
|
|
180
|
-
"colorDepth": int(client_data.get("screenProps", {}).get("colorDepth", 0))
|
|
181
|
-
}
|
|
182
|
-
})
|
|
183
|
-
|
|
184
|
-
# JSON序列化
|
|
185
|
-
class CompactJSONEncoder(json.JSONEncoder):
|
|
186
|
-
def encode(self, obj):
|
|
187
|
-
return json.dumps(obj, separators=(',', ':'), sort_keys=False)
|
|
188
|
-
|
|
189
|
-
data_str = CompactJSONEncoder().encode(standardized_data)
|
|
190
|
-
|
|
191
|
-
# SHA512哈希
|
|
192
|
-
sha512 = hashlib.sha512()
|
|
193
|
-
sha512.update(data_str.encode('utf-8'))
|
|
194
|
-
server_hash_str = sha512.hexdigest()
|
|
195
|
-
|
|
196
|
-
# 常量时间比较
|
|
197
|
-
if not hmac.compare_digest(server_hash_str.encode(), client_hash.encode()):
|
|
198
|
-
if logger_func:
|
|
199
|
-
logger_func(f"设备指纹不匹配: 期望长度={len(server_hash_str)}, 实际长度={len(client_hash)}")
|
|
200
|
-
return False
|
|
201
|
-
|
|
202
|
-
# 硬件验证
|
|
203
|
-
hw_concurrency = standardized_data["hardwareConcurrency"]
|
|
204
|
-
if not (1 <= hw_concurrency <= 128 and isinstance(hw_concurrency, int)):
|
|
205
|
-
if logger_func:
|
|
206
|
-
logger_func(f"硬件并发数异常: {hw_concurrency}")
|
|
207
|
-
return False
|
|
208
|
-
|
|
209
|
-
screen = standardized_data["screenProps"]
|
|
210
|
-
if not all(isinstance(v, int) and v > 0 for v in [screen["width"], screen["height"], screen["colorDepth"]]):
|
|
211
|
-
if logger_func:
|
|
212
|
-
logger_func(f"屏幕属性异常: {screen}")
|
|
213
|
-
return False
|
|
214
|
-
|
|
215
|
-
return True
|
|
216
|
-
|
|
217
|
-
except Exception as e:
|
|
218
|
-
if logger_func:
|
|
219
|
-
logger_func(f"设备指纹验证异常: {str(e)}")
|
|
220
|
-
return False
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
class CryptoManager:
|
|
224
|
-
"""
|
|
225
|
-
通用加密解密管理器
|
|
226
|
-
"""
|
|
78
|
+
def __new__(cls, *args, **kwargs):
|
|
79
|
+
if not cls._instance:
|
|
80
|
+
with cls._lock:
|
|
81
|
+
if not cls._instance:
|
|
82
|
+
cls._instance = super().__new__(cls)
|
|
83
|
+
return cls._instance
|
|
227
84
|
|
|
228
|
-
def __init__(self,
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
fingerprint_validator: Optional[FingerprintValidator] = None):
|
|
232
|
-
"""
|
|
233
|
-
初始化加密管理器
|
|
234
|
-
|
|
235
|
-
Args:
|
|
236
|
-
config: 加密配置对象(必需)
|
|
237
|
-
logger: 日志记录器,None时不使用日志
|
|
238
|
-
redis_client: Redis客户端,用于nonce防重放攻击
|
|
239
|
-
fingerprint_validator: 设备指纹验证器,None时使用默认dpflask验证器
|
|
240
|
-
"""
|
|
85
|
+
def __init__(self, config: CryptoConfig, logger: Any):
|
|
86
|
+
if hasattr(self, '_initialized'):
|
|
87
|
+
return
|
|
241
88
|
self.config = config
|
|
242
|
-
self.
|
|
243
|
-
self.
|
|
244
|
-
self.fingerprint_validator = fingerprint_validator or DpflaskFingerprintValidator()
|
|
245
|
-
|
|
246
|
-
self._init_redis()
|
|
247
|
-
# 密钥缓存初始化
|
|
248
|
-
self._init_cache()
|
|
249
|
-
|
|
250
|
-
# 验证环境
|
|
251
|
-
self._validate_environment()
|
|
252
|
-
|
|
253
|
-
logger.debug(f"CryptoManager初始化完成: keys_dir={self.keys_directory}, nonce_enabled={self.enable_nonce_check}")
|
|
89
|
+
self.logger = logger
|
|
90
|
+
self._initialized = True
|
|
254
91
|
|
|
255
|
-
def
|
|
256
|
-
"""
|
|
257
|
-
|
|
258
|
-
redis_password = parser.get_value(file_path=config_file, section='redis', key='password', value_type=str) # redis 使用本地数据,全部机子相同
|
|
259
|
-
# Redis连接配置, 创建连接池
|
|
260
|
-
import redis
|
|
261
|
-
redis_pool = redis.ConnectionPool(
|
|
262
|
-
host='127.0.0.1',
|
|
263
|
-
port=6379,
|
|
264
|
-
db=0,
|
|
265
|
-
password=redis_password,
|
|
266
|
-
max_connections=3, # 连接池最大连接数
|
|
267
|
-
socket_timeout=5, # 操作超时时间(秒)
|
|
268
|
-
socket_connect_timeout=3, # 连接超时时间(秒)
|
|
269
|
-
health_check_interval=30, # 健康检查间隔(秒)
|
|
270
|
-
decode_responses=False, # 保持二进制数据格式
|
|
271
|
-
)
|
|
272
|
-
|
|
273
|
-
# 通过连接池获取Redis实例,这个实例用于密钥服务
|
|
274
|
-
self.redis_client = redis.Redis(connection_pool=redis_pool)
|
|
275
|
-
|
|
276
|
-
def _init_cache(self) -> None:
|
|
277
|
-
"""初始化密钥缓存"""
|
|
278
|
-
self._key_cache_lock = threading.RLock()
|
|
279
|
-
self._public_key_cache = None
|
|
280
|
-
self._private_key_cache = None
|
|
281
|
-
self._cache_timestamp = 0
|
|
92
|
+
def get_public_key(self) -> Optional[str]:
|
|
93
|
+
"""获取公钥PEM字符串"""
|
|
94
|
+
cache_key = 'public_key'
|
|
282
95
|
|
|
283
|
-
#
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
error_msg = f"密钥目录不存在: {self.keys_directory}"
|
|
291
|
-
logger.error(error_msg)
|
|
292
|
-
raise FileNotFoundError(error_msg)
|
|
293
|
-
|
|
294
|
-
# ==================== 缓存管理方法 ====================
|
|
295
|
-
|
|
296
|
-
def _is_cache_valid(self) -> bool:
|
|
297
|
-
"""检查缓存是否有效"""
|
|
298
|
-
if not self.config.enable_key_cache:
|
|
299
|
-
return False
|
|
300
|
-
current_time = time.time()
|
|
301
|
-
return (self._cache_timestamp > 0 and
|
|
302
|
-
current_time - self._cache_timestamp < self.config.key_cache_ttl_seconds)
|
|
303
|
-
|
|
304
|
-
def clear_cache(self) -> None:
|
|
305
|
-
"""清除密钥缓存"""
|
|
306
|
-
with self._key_cache_lock:
|
|
307
|
-
self._public_key_cache = None
|
|
308
|
-
self._private_key_cache = None
|
|
309
|
-
self._cache_timestamp = 0
|
|
310
|
-
logger.debug("密钥缓存已清除")
|
|
311
|
-
|
|
312
|
-
# ==================== 密钥加载方法 ====================
|
|
313
|
-
|
|
314
|
-
def _load_public_key_from_file(self) -> Optional[str]:
|
|
315
|
-
"""从文件加载公钥"""
|
|
96
|
+
# 检查缓存
|
|
97
|
+
if self.config.enable_key_cache and cache_key in self._cache:
|
|
98
|
+
cache_time = self._cache_time.get(cache_key, 0)
|
|
99
|
+
if time.time() - cache_time < self.config.key_cache_ttl_seconds:
|
|
100
|
+
return self._cache[cache_key]
|
|
101
|
+
|
|
102
|
+
# 读取文件
|
|
316
103
|
try:
|
|
317
|
-
key_path = os.path.join(
|
|
104
|
+
key_path = os.path.join(
|
|
105
|
+
self.config.key_dir_path,
|
|
106
|
+
self.config.keys_subdir,
|
|
107
|
+
self.config.public_key_filename
|
|
108
|
+
)
|
|
318
109
|
|
|
319
|
-
|
|
320
|
-
|
|
110
|
+
with open(key_path, 'r', encoding='utf-8') as f:
|
|
111
|
+
public_key_pem = f.read().strip()
|
|
321
112
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
)
|
|
113
|
+
# 缓存结果
|
|
114
|
+
if self.config.enable_key_cache:
|
|
115
|
+
self._cache[cache_key] = public_key_pem
|
|
116
|
+
self._cache_time[cache_key] = time.time()
|
|
327
117
|
|
|
328
|
-
|
|
329
|
-
public_pem = public_key.public_bytes(
|
|
330
|
-
encoding=serialization.Encoding.PEM,
|
|
331
|
-
format=serialization.PublicFormat.SubjectPublicKeyInfo
|
|
332
|
-
)
|
|
333
|
-
|
|
334
|
-
return public_pem.decode("utf-8")
|
|
118
|
+
return public_key_pem
|
|
335
119
|
|
|
336
120
|
except Exception as e:
|
|
337
|
-
logger.error(f"
|
|
121
|
+
self.logger.error(f"读取公钥失败: {str(e)}")
|
|
338
122
|
return None
|
|
339
123
|
|
|
340
|
-
def
|
|
341
|
-
"""
|
|
124
|
+
def get_private_key(self) -> Optional[Any]:
|
|
125
|
+
"""获取私钥对象"""
|
|
126
|
+
cache_key = 'private_key'
|
|
127
|
+
|
|
128
|
+
# 检查缓存
|
|
129
|
+
if self.config.enable_key_cache and cache_key in self._cache:
|
|
130
|
+
cache_time = self._cache_time.get(cache_key, 0)
|
|
131
|
+
if time.time() - cache_time < self.config.key_cache_ttl_seconds:
|
|
132
|
+
return self._cache[cache_key]
|
|
133
|
+
|
|
134
|
+
# 读取文件
|
|
342
135
|
try:
|
|
343
|
-
key_path = os.path.join(
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
136
|
+
key_path = os.path.join(
|
|
137
|
+
self.config.key_dir_path,
|
|
138
|
+
self.config.keys_subdir,
|
|
139
|
+
self.config.private_key_filename
|
|
140
|
+
)
|
|
347
141
|
|
|
348
|
-
with open(key_path,
|
|
142
|
+
with open(key_path, 'rb') as f:
|
|
349
143
|
private_key = serialization.load_pem_private_key(
|
|
350
|
-
|
|
144
|
+
f.read(),
|
|
351
145
|
password=None,
|
|
352
146
|
backend=default_backend()
|
|
353
147
|
)
|
|
354
148
|
|
|
149
|
+
# 缓存结果
|
|
150
|
+
if self.config.enable_key_cache:
|
|
151
|
+
self._cache[cache_key] = private_key
|
|
152
|
+
self._cache_time[cache_key] = time.time()
|
|
153
|
+
|
|
355
154
|
return private_key
|
|
356
155
|
|
|
357
156
|
except Exception as e:
|
|
358
|
-
logger.error(f"
|
|
157
|
+
self.logger.error(f"读取私钥失败: {str(e)}")
|
|
359
158
|
return None
|
|
360
159
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
return self._public_key_cache
|
|
372
|
-
|
|
373
|
-
# 从文件加载
|
|
374
|
-
public_key = self._load_public_key_from_file()
|
|
375
|
-
|
|
376
|
-
# 更新缓存
|
|
377
|
-
if public_key and self.config.enable_key_cache:
|
|
378
|
-
self._public_key_cache = public_key
|
|
379
|
-
self._cache_timestamp = time.time()
|
|
380
|
-
logger.debug("公钥已缓存")
|
|
381
|
-
|
|
382
|
-
return public_key
|
|
383
|
-
|
|
384
|
-
def get_keys_info(self) -> Dict[str, Any]:
|
|
385
|
-
"""
|
|
386
|
-
获取密钥文件信息
|
|
387
|
-
"""
|
|
388
|
-
public_key_path = os.path.join(self.keys_directory, f'{self.config.public_key_filename}.pem')
|
|
389
|
-
private_key_path = os.path.join(self.keys_directory, f'{self.config.private_key_filename}.pem')
|
|
390
|
-
|
|
391
|
-
return {
|
|
392
|
-
'keys_directory': self.keys_directory,
|
|
393
|
-
'public_key_path': public_key_path,
|
|
394
|
-
'private_key_path': private_key_path,
|
|
395
|
-
'public_key_exists': os.path.exists(public_key_path),
|
|
396
|
-
'private_key_exists': os.path.exists(private_key_path),
|
|
397
|
-
'public_key_size': os.path.getsize(public_key_path) if os.path.exists(public_key_path) else 0,
|
|
398
|
-
'private_key_size': os.path.getsize(private_key_path) if os.path.exists(private_key_path) else 0,
|
|
399
|
-
'cache_enabled': self.config.enable_key_cache,
|
|
400
|
-
'cache_ttl_seconds': self.config.key_cache_ttl_seconds,
|
|
401
|
-
'nonce_enabled': self.enable_nonce_check
|
|
402
|
-
}
|
|
160
|
+
def clear_cache(self):
|
|
161
|
+
"""清除缓存"""
|
|
162
|
+
self._cache.clear()
|
|
163
|
+
self._cache_time.clear()
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
# ==================== 加密服务 ====================
|
|
167
|
+
|
|
168
|
+
class CryptoService:
|
|
169
|
+
"""加密服务"""
|
|
403
170
|
|
|
404
|
-
|
|
171
|
+
def __init__(self, key_manager: KeyManager, logger: Any):
|
|
172
|
+
self.key_manager = key_manager
|
|
173
|
+
self.logger = logger
|
|
405
174
|
|
|
406
|
-
def
|
|
407
|
-
"""
|
|
408
|
-
解密载荷数据
|
|
409
|
-
"""
|
|
175
|
+
def decrypt_token(self, encrypted_token: str) -> Optional[Dict[str, Any]]:
|
|
176
|
+
"""解密令牌"""
|
|
410
177
|
try:
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
return None
|
|
178
|
+
# 解析加密数据
|
|
179
|
+
encrypted_data = json.loads(base64.b64decode(encrypted_token))
|
|
414
180
|
|
|
415
|
-
#
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
logger.debug("使用缓存的私钥")
|
|
419
|
-
private_key = self._private_key_cache
|
|
420
|
-
else:
|
|
421
|
-
private_key = self._load_private_key_from_file()
|
|
422
|
-
if not private_key:
|
|
423
|
-
return None
|
|
424
|
-
|
|
425
|
-
# 更新缓存
|
|
426
|
-
if self.config.enable_key_cache:
|
|
427
|
-
self._private_key_cache = private_key
|
|
428
|
-
if self._cache_timestamp == 0: # 首次缓存时间戳
|
|
429
|
-
self._cache_timestamp = time.time()
|
|
430
|
-
logger.debug("私钥已缓存")
|
|
431
|
-
|
|
432
|
-
# 解析加密令牌
|
|
433
|
-
try:
|
|
434
|
-
token_data = json.loads(base64.b64decode(encrypted_token).decode())
|
|
435
|
-
except (json.JSONDecodeError, ValueError, base64.binascii.Error) as e:
|
|
436
|
-
logger.warning(f"令牌格式错误: {type(e).__name__}")
|
|
181
|
+
# 获取私钥
|
|
182
|
+
private_key = self.key_manager.get_private_key()
|
|
183
|
+
if not private_key:
|
|
437
184
|
return None
|
|
438
185
|
|
|
439
|
-
#
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
try:
|
|
448
|
-
encrypted_key = base64.b64decode(token_data['key'])
|
|
449
|
-
iv = base64.b64decode(token_data['iv'])
|
|
450
|
-
ciphertext = base64.b64decode(token_data['ciphertext'])
|
|
451
|
-
tag = base64.b64decode(token_data['tag'])
|
|
452
|
-
except (ValueError, base64.binascii.Error) as e:
|
|
453
|
-
logger.warning(f"令牌组件Base64解码失败: {type(e).__name__}")
|
|
454
|
-
return None
|
|
455
|
-
|
|
456
|
-
# 使用RSA-OAEP解密AES密钥
|
|
457
|
-
try:
|
|
458
|
-
raw_key = private_key.decrypt(
|
|
459
|
-
encrypted_key,
|
|
460
|
-
padding.OAEP(
|
|
461
|
-
mgf=padding.MGF1(algorithm=hashes.SHA512()),
|
|
462
|
-
algorithm=hashes.SHA512(),
|
|
463
|
-
label=None
|
|
464
|
-
)
|
|
186
|
+
# 解密AES密钥
|
|
187
|
+
encrypted_aes_key = base64.b64decode(encrypted_data['key'])
|
|
188
|
+
aes_key = private_key.decrypt(
|
|
189
|
+
encrypted_aes_key,
|
|
190
|
+
padding.OAEP(
|
|
191
|
+
mgf=padding.MGF1(algorithm=hashes.SHA256()),
|
|
192
|
+
algorithm=hashes.SHA256(),
|
|
193
|
+
label=None
|
|
465
194
|
)
|
|
466
|
-
|
|
467
|
-
logger.warning(f"RSA解密失败: {type(e).__name__}")
|
|
468
|
-
return None
|
|
195
|
+
)
|
|
469
196
|
|
|
470
|
-
#
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
decrypted = aesgcm.decrypt(iv, ciphertext + tag, None)
|
|
474
|
-
except Exception as e:
|
|
475
|
-
logger.warning(f"AES-GCM解密失败: {type(e).__name__}")
|
|
476
|
-
return None
|
|
197
|
+
# 解密数据
|
|
198
|
+
iv = base64.b64decode(encrypted_data['iv'])
|
|
199
|
+
ciphertext = base64.b64decode(encrypted_data['ciphertext'])
|
|
477
200
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
logger.warning(f"载荷JSON解析失败: {type(e).__name__}")
|
|
485
|
-
return None
|
|
201
|
+
aesgcm = AESGCM(aes_key)
|
|
202
|
+
decrypted_data = aesgcm.decrypt(iv, ciphertext, None)
|
|
203
|
+
|
|
204
|
+
# 解析JSON
|
|
205
|
+
payload = json.loads(decrypted_data.decode('utf-8'))
|
|
206
|
+
return payload
|
|
486
207
|
|
|
487
208
|
except Exception as e:
|
|
488
|
-
logger.error(f"
|
|
209
|
+
self.logger.error(f"解密失败: {str(e)}")
|
|
489
210
|
return None
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
# ==================== 验证服务 ====================
|
|
214
|
+
|
|
215
|
+
class Validator:
|
|
216
|
+
"""验证器"""
|
|
490
217
|
|
|
491
|
-
def
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
218
|
+
def __init__(self, config: CryptoConfig, redis_client: Any, logger: Any):
|
|
219
|
+
self.config = config
|
|
220
|
+
self.redis_client = redis_client
|
|
221
|
+
self.logger = logger
|
|
222
|
+
|
|
223
|
+
def validate_timestamp(self, payload: Dict[str, Any]) -> bool:
|
|
224
|
+
"""验证时间戳"""
|
|
495
225
|
try:
|
|
496
|
-
if not isinstance(payload, dict):
|
|
497
|
-
return False
|
|
498
|
-
|
|
499
|
-
# 1. 时间窗口验证
|
|
500
226
|
current_time = int(time.time())
|
|
501
|
-
payload_timestamp = payload.get('timestamp'
|
|
227
|
+
payload_timestamp = payload.get('timestamp')
|
|
502
228
|
|
|
503
229
|
if not isinstance(payload_timestamp, (int, float)):
|
|
504
230
|
return False
|
|
505
231
|
|
|
506
232
|
time_diff = abs(current_time - int(payload_timestamp))
|
|
507
|
-
|
|
233
|
+
return time_diff <= self.config.time_window_seconds
|
|
234
|
+
|
|
235
|
+
except Exception:
|
|
236
|
+
return False
|
|
237
|
+
|
|
238
|
+
def validate_nonce(self, payload: Dict[str, Any]) -> bool:
|
|
239
|
+
"""验证nonce(防重放)"""
|
|
240
|
+
if not self.config.enable_nonce_check or not self.redis_client:
|
|
241
|
+
return True
|
|
242
|
+
|
|
243
|
+
try:
|
|
244
|
+
nonce = payload.get('nonce')
|
|
245
|
+
if not nonce:
|
|
508
246
|
return False
|
|
509
247
|
|
|
510
|
-
|
|
511
|
-
def log_fingerprint_error(msg):
|
|
512
|
-
logger.warning(f"设备指纹验证: {msg}")
|
|
248
|
+
nonce_key = f"{self.config.nonce_redis_prefix}:{nonce}"
|
|
513
249
|
|
|
514
|
-
|
|
250
|
+
# 检查nonce是否已存在
|
|
251
|
+
if self.redis_client.exists(nonce_key):
|
|
515
252
|
return False
|
|
516
253
|
|
|
517
|
-
#
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
if self._is_nonce_used(nonce, token):
|
|
524
|
-
return False
|
|
525
|
-
|
|
526
|
-
logger.debug("载荷验证通过")
|
|
254
|
+
# 设置nonce(防止重复使用)
|
|
255
|
+
self.redis_client.setex(
|
|
256
|
+
nonce_key,
|
|
257
|
+
self.config.nonce_expire_seconds,
|
|
258
|
+
"used"
|
|
259
|
+
)
|
|
527
260
|
return True
|
|
528
261
|
|
|
529
262
|
except Exception as e:
|
|
530
|
-
logger.error(f"
|
|
263
|
+
self.logger.error(f"Nonce验证失败: {str(e)}")
|
|
531
264
|
return False
|
|
532
265
|
|
|
533
|
-
def
|
|
534
|
-
"""
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
name=redis_key,
|
|
545
|
-
value=token,
|
|
546
|
-
ex=self.config.nonce_expire_seconds,
|
|
547
|
-
nx=True # 仅当键不存在时设置
|
|
548
|
-
)
|
|
549
|
-
is_used = not bool(result) # False表示设置成功(未使用过)
|
|
550
|
-
if is_used:
|
|
551
|
-
logger.warning(f"nonce重复使用: {nonce[:8]}...")
|
|
552
|
-
return is_used
|
|
553
|
-
except Exception as e:
|
|
554
|
-
logger.error(f"nonce检查异常: {str(e)}")
|
|
555
|
-
return True # 异常时认为已使用,拒绝请求
|
|
266
|
+
def validate(self, payload: Dict[str, Any]) -> bool:
|
|
267
|
+
"""执行所有验证"""
|
|
268
|
+
return (self.validate_timestamp(payload) and
|
|
269
|
+
self.validate_nonce(payload))
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
# ==================== 主要管理器 ====================
|
|
273
|
+
|
|
274
|
+
class OptimizedCryptoManager:
|
|
275
|
+
"""
|
|
276
|
+
加密管理器
|
|
556
277
|
|
|
557
|
-
|
|
278
|
+
特点:
|
|
279
|
+
1. 复用外部Redis连接,避免重复创建连接池
|
|
280
|
+
2. 单例密钥管理,避免重复读取文件
|
|
281
|
+
"""
|
|
558
282
|
|
|
559
|
-
def
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
283
|
+
def __init__(self, config: CryptoConfig, redis_client: Any = None, logger: Any = None):
|
|
284
|
+
self.config = config
|
|
285
|
+
self.redis_client = redis_client
|
|
286
|
+
|
|
287
|
+
# 初始化日志器
|
|
288
|
+
if logger is None:
|
|
289
|
+
self.logger = mylogger.MyLogger(
|
|
290
|
+
logging_mode='file',
|
|
291
|
+
log_level='info',
|
|
292
|
+
log_format='json',
|
|
293
|
+
max_log_size=50,
|
|
294
|
+
backup_count=5,
|
|
295
|
+
enable_async=False,
|
|
296
|
+
sample_rate=1,
|
|
297
|
+
sensitive_fields=[],
|
|
298
|
+
enable_metrics=False,
|
|
299
|
+
)
|
|
300
|
+
else:
|
|
301
|
+
self.logger = logger
|
|
302
|
+
|
|
303
|
+
# 初始化组件
|
|
304
|
+
self.key_manager = KeyManager(config, self.logger)
|
|
305
|
+
self.crypto_service = CryptoService(self.key_manager, self.logger)
|
|
306
|
+
self.validator = Validator(config, redis_client, self.logger)
|
|
307
|
+
|
|
308
|
+
self.logger.debug("OptimizedCryptoManager初始化完成")
|
|
566
309
|
|
|
567
|
-
|
|
568
|
-
"""
|
|
569
|
-
认证令牌并返回详细结果(向后兼容)
|
|
570
|
-
"""
|
|
571
|
-
result = self.authenticate_token_detailed(token)
|
|
572
|
-
return {
|
|
573
|
-
'success': result.success,
|
|
574
|
-
'error': result.result_code.value if not result.success else None,
|
|
575
|
-
'message': result.error_details or ('认证成功' if result.success else '认证失败'),
|
|
576
|
-
'payload': result.payload
|
|
577
|
-
}
|
|
310
|
+
# ==================== 主要API ====================
|
|
578
311
|
|
|
579
|
-
def
|
|
312
|
+
def authenticate_token(self, token: str) -> AuthenticationResult:
|
|
580
313
|
"""
|
|
581
|
-
|
|
314
|
+
认证令牌 - 主要的认证API
|
|
315
|
+
|
|
316
|
+
Args:
|
|
317
|
+
token: 加密的令牌字符串
|
|
318
|
+
|
|
319
|
+
Returns:
|
|
320
|
+
AuthenticationResult: 标准化的认证结果
|
|
582
321
|
"""
|
|
583
322
|
start_time = time.time()
|
|
584
323
|
|
|
585
324
|
try:
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
ValidationResult.INVALID_PAYLOAD,
|
|
589
|
-
"令牌为空",
|
|
590
|
-
(time.time() - start_time) * 1000
|
|
591
|
-
)
|
|
592
|
-
|
|
593
|
-
# 1. 解密载荷
|
|
594
|
-
payload = self.decrypt_payload(token)
|
|
325
|
+
# 1. 解密令牌
|
|
326
|
+
payload = self.crypto_service.decrypt_token(token)
|
|
595
327
|
if not payload:
|
|
596
|
-
return
|
|
597
|
-
ValidationResult.DECRYPT_FAILED,
|
|
328
|
+
return AuthenticationResult.failure_result(
|
|
598
329
|
"令牌解密失败",
|
|
599
330
|
(time.time() - start_time) * 1000
|
|
600
331
|
)
|
|
601
332
|
|
|
602
333
|
# 2. 验证载荷
|
|
603
|
-
if not self.
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
payload_timestamp = payload.get('timestamp', 0)
|
|
607
|
-
|
|
608
|
-
if abs(current_time - int(payload_timestamp)) > self.config.time_window_seconds:
|
|
609
|
-
return AuthResult.failure_result(
|
|
610
|
-
ValidationResult.TIMESTAMP_INVALID,
|
|
611
|
-
"时间窗口验证失败",
|
|
612
|
-
(time.time() - start_time) * 1000
|
|
613
|
-
)
|
|
614
|
-
|
|
615
|
-
# 检查nonce
|
|
616
|
-
if self.enable_nonce_check and token:
|
|
617
|
-
nonce = payload.get('nonce')
|
|
618
|
-
if nonce and self._is_nonce_used(nonce, token):
|
|
619
|
-
return AuthResult.failure_result(
|
|
620
|
-
ValidationResult.NONCE_REUSED,
|
|
621
|
-
"nonce重复使用",
|
|
622
|
-
(time.time() - start_time) * 1000
|
|
623
|
-
)
|
|
624
|
-
|
|
625
|
-
# 其他情况(设备指纹验证失败等)
|
|
626
|
-
return AuthResult.failure_result(
|
|
627
|
-
ValidationResult.FINGERPRINT_MISMATCH,
|
|
628
|
-
"设备指纹验证失败",
|
|
334
|
+
if not self.validator.validate(payload):
|
|
335
|
+
return AuthenticationResult.failure_result(
|
|
336
|
+
"载荷验证失败",
|
|
629
337
|
(time.time() - start_time) * 1000
|
|
630
338
|
)
|
|
631
339
|
|
|
632
|
-
# 3.
|
|
340
|
+
# 3. 认证成功
|
|
633
341
|
execution_time = (time.time() - start_time) * 1000
|
|
634
|
-
logger.debug(f"
|
|
342
|
+
self.logger.debug(f"认证成功,耗时: {execution_time:.2f}ms")
|
|
635
343
|
|
|
636
|
-
return
|
|
344
|
+
return AuthenticationResult.success_result(payload, execution_time)
|
|
637
345
|
|
|
638
346
|
except Exception as e:
|
|
639
347
|
execution_time = (time.time() - start_time) * 1000
|
|
640
|
-
|
|
348
|
+
error_msg = f"认证异常: {str(e)}"
|
|
349
|
+
self.logger.error(error_msg)
|
|
641
350
|
|
|
642
|
-
return
|
|
643
|
-
ValidationResult.DECRYPT_FAILED,
|
|
644
|
-
f"认证异常: {str(e)}",
|
|
645
|
-
execution_time,
|
|
646
|
-
{"exception_type": type(e).__name__}
|
|
647
|
-
)
|
|
351
|
+
return AuthenticationResult.failure_result(error_msg, execution_time)
|
|
648
352
|
|
|
649
|
-
def
|
|
353
|
+
def decrypt_payload(self, encrypted_token: str) -> Optional[Dict[str, Any]]:
|
|
650
354
|
"""
|
|
651
|
-
|
|
355
|
+
仅解密载荷数据(不进行验证)
|
|
356
|
+
|
|
357
|
+
Args:
|
|
358
|
+
encrypted_token: 加密的令牌字符串
|
|
359
|
+
|
|
360
|
+
Returns:
|
|
361
|
+
解密后的载荷数据或None
|
|
652
362
|
"""
|
|
653
|
-
|
|
654
|
-
|
|
363
|
+
return self.crypto_service.decrypt_token(encrypted_token)
|
|
364
|
+
|
|
365
|
+
def get_public_key(self) -> Optional[str]:
|
|
366
|
+
"""获取PEM格式的公钥字符串"""
|
|
367
|
+
return self.key_manager.get_public_key()
|
|
368
|
+
|
|
369
|
+
# ==================== 向后兼容API(简化版)====================
|
|
370
|
+
|
|
371
|
+
def get_private_key(self, token: str) -> bool:
|
|
372
|
+
"""验证token成功性"""
|
|
373
|
+
result = self.authenticate_token(token)
|
|
374
|
+
return result.success
|
|
375
|
+
|
|
376
|
+
def decrypt_token(self, token: str, time_window: int = None, return_data: bool = True) -> Union[bool, Dict[str, Any]]:
|
|
377
|
+
"""向后兼容方法"""
|
|
378
|
+
result = self.authenticate_token(token)
|
|
379
|
+
if return_data:
|
|
380
|
+
return result.payload if result.success else None
|
|
381
|
+
return result.success
|
|
382
|
+
|
|
383
|
+
# ==================== 系统管理API ====================
|
|
384
|
+
|
|
385
|
+
def get_system_status(self) -> Dict[str, Any]:
|
|
386
|
+
"""获取系统状态信息"""
|
|
387
|
+
redis_healthy = False
|
|
388
|
+
if self.redis_client:
|
|
389
|
+
try:
|
|
390
|
+
self.redis_client.ping()
|
|
391
|
+
redis_healthy = True
|
|
392
|
+
except Exception:
|
|
393
|
+
redis_healthy = False
|
|
655
394
|
|
|
656
395
|
return {
|
|
657
|
-
'
|
|
658
|
-
'
|
|
659
|
-
'private_key_path': private_key_path,
|
|
660
|
-
'public_key_exists': os.path.exists(public_key_path),
|
|
661
|
-
'private_key_exists': os.path.exists(private_key_path),
|
|
662
|
-
'public_key_size': os.path.getsize(public_key_path) if os.path.exists(public_key_path) else 0,
|
|
663
|
-
'private_key_size': os.path.getsize(private_key_path) if os.path.exists(private_key_path) else 0,
|
|
396
|
+
'redis_healthy': redis_healthy,
|
|
397
|
+
'keys_available': bool(self.key_manager.get_public_key()),
|
|
664
398
|
'cache_enabled': self.config.enable_key_cache,
|
|
665
|
-
'
|
|
666
|
-
'
|
|
399
|
+
'nonce_check_enabled': self.config.enable_nonce_check,
|
|
400
|
+
'time_window_seconds': self.config.time_window_seconds,
|
|
401
|
+
'version': '2.1.0-optimized'
|
|
667
402
|
}
|
|
403
|
+
|
|
404
|
+
def clear_cache(self) -> None:
|
|
405
|
+
"""清除所有缓存"""
|
|
406
|
+
self.key_manager.clear_cache()
|
|
407
|
+
self.logger.debug("所有缓存已清除")
|
|
668
408
|
|
|
669
409
|
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
410
|
+
# ==================== 便捷的创建函数 ====================
|
|
411
|
+
|
|
412
|
+
def create_crypto_manager(config: Optional[CryptoConfig] = None,
|
|
413
|
+
redis_client: Any = None) -> OptimizedCryptoManager:
|
|
673
414
|
"""
|
|
415
|
+
创建函数
|
|
674
416
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
生成安全的随机令牌
|
|
679
|
-
|
|
680
|
-
Args:
|
|
681
|
-
length (int): 令牌长度,默认32字节
|
|
682
|
-
|
|
683
|
-
Returns:
|
|
684
|
-
str: 十六进制格式的随机令牌
|
|
685
|
-
"""
|
|
686
|
-
import secrets
|
|
687
|
-
return secrets.token_hex(length)
|
|
688
|
-
|
|
689
|
-
@staticmethod
|
|
690
|
-
def hash_data(data: Union[str, bytes], algorithm: str = 'sha256') -> str:
|
|
691
|
-
"""
|
|
692
|
-
对数据进行哈希处理
|
|
693
|
-
|
|
694
|
-
Args:
|
|
695
|
-
data (str|bytes): 要哈希的数据
|
|
696
|
-
algorithm (str): 哈希算法,支持 'md5', 'sha1', 'sha256', 'sha512'
|
|
697
|
-
|
|
698
|
-
Returns:
|
|
699
|
-
str: 十六进制格式的哈希值
|
|
700
|
-
"""
|
|
701
|
-
if isinstance(data, str):
|
|
702
|
-
data = data.encode('utf-8')
|
|
703
|
-
|
|
704
|
-
hash_func = getattr(hashlib, algorithm.lower())()
|
|
705
|
-
hash_func.update(data)
|
|
706
|
-
return hash_func.hexdigest()
|
|
707
|
-
|
|
708
|
-
@staticmethod
|
|
709
|
-
def encode_base64(data: Union[str, bytes]) -> str:
|
|
710
|
-
"""
|
|
711
|
-
Base64编码
|
|
417
|
+
Args:
|
|
418
|
+
config: 可选的配置对象,如果不提供则使用默认配置
|
|
419
|
+
redis_client: 可选的Redis客户端,如果提供则复用连接
|
|
712
420
|
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
"""
|
|
719
|
-
if isinstance(data, str):
|
|
720
|
-
data = data.encode('utf-8')
|
|
721
|
-
return base64.b64encode(data).decode('utf-8')
|
|
421
|
+
Returns:
|
|
422
|
+
配置好的OptimizedCryptoManager实例
|
|
423
|
+
"""
|
|
424
|
+
if config is None:
|
|
425
|
+
config = CryptoConfig()
|
|
722
426
|
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
"""
|
|
726
|
-
Base64解码
|
|
727
|
-
|
|
728
|
-
Args:
|
|
729
|
-
encoded_data (str): Base64编码的字符串
|
|
730
|
-
|
|
731
|
-
Returns:
|
|
732
|
-
bytes: 解码后的字节数据
|
|
733
|
-
"""
|
|
734
|
-
return base64.b64decode(encoded_data)
|
|
427
|
+
return OptimizedCryptoManager(config, redis_client)
|
|
428
|
+
|
|
735
429
|
|
|
430
|
+
# ==================== 示例用法 ====================
|
|
736
431
|
|
|
737
432
|
if __name__ == "__main__":
|
|
738
|
-
|
|
433
|
+
# 示例:使用默认配置创建加密管理器
|
|
434
|
+
crypto_manager = create_crypto_manager()
|
|
435
|
+
|
|
436
|
+
# 示例:获取系统状态
|
|
437
|
+
status = crypto_manager.get_system_status()
|
|
438
|
+
print(f"系统状态: {status}")
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
mdbq/__init__.py,sha256=Il5Q9ATdX8yXqVxtP_nYqUhExzxPC_qk_WXQ_4h0exg,16
|
|
2
|
-
mdbq/__version__.py,sha256=
|
|
2
|
+
mdbq/__version__.py,sha256=8InfWPXvxAxerWannxoYZduGNW4ULDSvL7ME9Q09f44,19
|
|
3
3
|
mdbq/auth/__init__.py,sha256=pnPMAt63sh1B6kEvmutUuro46zVf2v2YDAG7q-jV_To,24
|
|
4
4
|
mdbq/auth/auth_backend.py,sha256=FGyl3EYcVAqHuCd5oojFC9A4Tl88F0YVAfMIKEfforQ,97822
|
|
5
|
-
mdbq/auth/crypto.py,sha256=
|
|
5
|
+
mdbq/auth/crypto.py,sha256=7KdWWSHuqsPTvycxUbB6y4nfKeaVXAqvqKmvUyQ404I,14279
|
|
6
6
|
mdbq/auth/rate_limiter.py,sha256=1m_Paxp8pDNpmyoFGRpFMVOJpbmeIvfVcfiQ2oH72qM,32850
|
|
7
7
|
mdbq/js/__init__.py,sha256=hpMi3_ZKwIWkzc0LnKL-SY9AS-7PYFHq0izYTgEvxjc,30
|
|
8
8
|
mdbq/js/jc.py,sha256=6Rgf1WqaJJ1oevpn-pt08gXKbX5hjoQaV6uZGCAGbYw,13177
|
|
@@ -35,7 +35,7 @@ mdbq/route/routes.py,sha256=QVGfTvDgu0CpcKCvk1ra74H8uojgqTLUav1fnVAqLEA,29433
|
|
|
35
35
|
mdbq/selenium/__init__.py,sha256=AKzeEceqZyvqn2dEDoJSzDQnbuENkJSHAlbHAD0u0ZI,10
|
|
36
36
|
mdbq/selenium/get_driver.py,sha256=1NTlVUE6QsyjTrVVVqTO2LOnYf578ccFWlWnvIXGtic,20903
|
|
37
37
|
mdbq/spider/__init__.py,sha256=RBMFXGy_jd1HXZhngB2T2XTvJqki8P_Fr-pBcwijnew,18
|
|
38
|
-
mdbq-4.0.
|
|
39
|
-
mdbq-4.0.
|
|
40
|
-
mdbq-4.0.
|
|
41
|
-
mdbq-4.0.
|
|
38
|
+
mdbq-4.0.125.dist-info/METADATA,sha256=p2oLQCT1qiJmkcU13sQ5h_E_KO3tja4Ij-FsXQ9jno4,365
|
|
39
|
+
mdbq-4.0.125.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
40
|
+
mdbq-4.0.125.dist-info/top_level.txt,sha256=2FQ-uLnCSB-OwFiWntzmwosW3X2Xqsg0ewh1axsaylA,5
|
|
41
|
+
mdbq-4.0.125.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|