This commit is contained in:
liubiren 2026-05-19 22:01:52 +08:00
parent 48db31f634
commit a4c76c576d
4 changed files with 138 additions and 31 deletions

View File

@ -56,7 +56,6 @@ class MySQL:
text(sql), connection, coerce_float=False
) # 不尝试将非整数数值转为浮点维持DECIMAL
return dataframe
except:
connection.rollback()
raise RuntimeError("执行SQL查询并返回DATAFRAME发生其它异常")

View File

@ -5,9 +5,8 @@
import json
from pathlib import Path
import sys
import time
from typing import Any, Callable, Dict, Generator, Literal, Optional, Tuple, Union
from typing import Any, Dict, Generator, Literal, Optional, Tuple, Union
from xml.etree import ElementTree
from pydantic import BaseModel, Field, HttpUrl, model_validator
@ -15,6 +14,9 @@ from requests import Response, Session
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
import sys
sys.path.append(Path(__file__).parent.as_posix())
from restrict import restrict
from sqlite import SQLite
@ -24,7 +26,9 @@ class Parameters(BaseModel):
请求参数模型
"""
url: HttpUrl = Field(default=..., description="统一资源定位符基于HttpUrl自动校验")
url: HttpUrl = Field(
default=..., description="统一资源定位符,基于 HttpUrl 自动校验"
)
params: Optional[Dict[str, Any]] = Field(default=None, description="查询参数")
headers: Optional[Dict[str, Any]] = Field(default=None, description="请求头")
data: Optional[Union[str, bytes, Dict[str, Any]]] = Field(
@ -46,25 +50,28 @@ class Parameters(BaseModel):
default=None,
description="上传文件",
)
encryption_strategy: Optional[Literal["rsa-aes-gcm"]] = Field(
default=None, description="加密策略,默认为不加密"
)
stream: Optional[bool] = Field(
default=None, description="开启流式传输,默认为不开启"
)
guid: Optional[str] = Field(default=None, description="缓存全局唯一标识")
guid: Optional[str] = Field(default=None, description="缓存唯一标识")
stream: bool = Field(default=False, description="开启流式传输,默认为不开启")
@model_validator(mode="after")
def validate_data(self):
"""校验data 数据和JSON数据互斥"""
def validate_data_json(self):
"""校验data 数据和 JSON 数据不能同时传入"""
if self.data and self.json_:
raise ValueError("data 数据和 JSON 数据不能同时使用")
raise ValueError("data 数据和 JSON 数据不能同时传入")
return self
@model_validator(mode="after")
def validate_files(self):
def validate_files_stream(self):
"""校验:上传文件和开启流式传输不能同时传入"""
if self.files and self.stream:
raise ValueError("上传文件和开启流式传输不能同时使用")
raise ValueError("上传文件和开启流式传输不能同时传入")
return self
@model_validator(mode="after")
def validate_guid_stream(self):
"""校验:缓存唯一标识和开启流式传输不能同时传入"""
if self.guid and self.stream:
raise ValueError("缓存唯一标识和开启流式传输不能同时传入")
return self
@ -130,7 +137,7 @@ class Caches(SQLite):
def query(self, guid: str) -> Optional[Dict[str, Any]]:
"""
查询并返回单条缓存
查询并返回缓存
:param guid: 缓存唯一标识
:return: 缓存
"""
@ -147,12 +154,12 @@ class Caches(SQLite):
return None if result is None else json.loads(result["cache"])
except Exception as exception:
raise RuntimeError(
f"查询并获取单条缓存发生异常:{str(exception)}"
f"查询并获取缓存发生异常:{str(exception)}"
) from exception
def update(self, guid: str, cache: Dict) -> Optional[bool]:
"""
新增或更新单条缓存若无则新增缓存若有则更新缓存
新增或更新缓存若无则新增缓存若有则更新缓存
:param guid: 缓存唯一标识
:param cache: 缓存
:return: 成功返回True失败返回False
@ -186,7 +193,6 @@ class Request:
total: int = 3,
backoff_factor: float = 0.5,
timeout: int = 60,
encryption_strategy: Optional[Literal["rsa-aes-gcm"]] = None,
cache_enabled: bool = False,
cache_ttl: int = 360,
):
@ -195,7 +201,6 @@ class Request:
:param total: 最大重试次数默认 3
:param backoff_factor: 重试间隔退避因子默认 0.5
:param timeout: 超时时间单位为秒默认为 60
:param encryption_strategy: 加密策略默认为不加密
:param cache_enabled: 使用缓存默认 False
:param cache_ttl: 缓存生存时间单位为天默认为 360
"""
@ -206,9 +211,6 @@ class Request:
# 初始化超时时间
self.timeout = timeout
# 初始化加密策略
self.encryption_strategy = encryption_strategy
# 实例化缓存
self.caches = Caches(cache_ttl=cache_ttl * 86400) if cache_enabled else None
@ -327,9 +329,9 @@ class Request:
if kwargs.get("json"):
kwargs["json"] = {k: v for k, v in kwargs["json"].items() if v}
# 缓存全局唯一标识
# 缓存唯一标识
guid = kwargs.pop("guid", None)
# 若缓存非空且缓存全局唯一标识非空则查询并获取单条缓存
# 若缓存非空且缓存唯一标识非空则查询并获取单条缓存
if self.caches and guid:
cache = self.caches.query(guid)
if cache:
@ -348,12 +350,11 @@ class Request:
# 处理响应对象
response = self._process_response(response=response)
# 若使用缓存且缓存全局唯一标识非空则新增或更新缓存
# 若使用缓存且缓存唯一标识非空则新增或更新缓存
if self.caches and guid:
self.caches.update(guid, response)
return response
# 重构异常信息
except Exception as exception:
response = getattr(
exception, "response", None

View File

@ -4,7 +4,111 @@
"""
# 列举导入模块
import uvicorn
from base64 import b64encode
from json import dumps
from pathlib import Path
from secrets import token_bytes
from time import time
from typing import cast
from typing import Any, Dict
from uuid import uuid4
def request():
pass
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives.serialization import load_pem_public_key
import sys
sys.path.append(Path(__file__).parent.parent.as_posix())
from utils.request import Request
def encrypt(payload: Dict[str, Any]) -> Dict[str, Any]:
"""
加密载荷
:param payload: 载荷
:return: 加密后载荷
"""
pem_path = Path(__file__).parent / "rsa_public_key.pem"
if not pem_path.exists():
raise FileNotFoundError("RSA 公钥 PEM 文件不存在")
# RSA 公钥
rsa_public_key = pem_path.read_text(encoding="utf-8")
# 实例 RSA 加密器
rsa_encryptor = cast(
rsa.RSAPublicKey,
load_pem_public_key(
data=rsa_public_key.encode("utf-8"), backend=default_backend()
),
)
# 生成 AES-256 密钥
aes_key = token_bytes(32)
# 使用 RSA 公钥加密 AES-256 密钥
aes_key_encrypted = rsa_encryptor.encrypt(
plaintext=aes_key,
padding=padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None,
),
)
aes_key_encoded = b64encode(aes_key_encrypted).decode() # BASE64 编码
# 初始向量
iv = token_bytes(12)
iv_encoded = b64encode(iv).decode() # BASE64 编码
# 实例 AES-GCM 加密器
aes_gcm_encryptor = Cipher(
algorithm=algorithms.AES(aes_key), mode=modes.GCM(iv), backend=default_backend()
).encryptor()
# 时间戳和随机码
timestamp, nonce = int(time()), uuid4().hex.lower()
# 附加认证
aes_gcm_encryptor.authenticate_additional_data(
data=f"timestamp={timestamp}&nonce={nonce}".encode("utf-8")
)
ciphertext = (
aes_gcm_encryptor.update(
data=dumps(payload, ensure_ascii=False).encode("utf-8")
)
+ aes_gcm_encryptor.finalize()
)
ciphertext_encoded = b64encode(ciphertext).decode() # BASE64 编码
tag = aes_gcm_encryptor.tag
tag_encoded = b64encode(tag).decode() # BASE64 编码
return {
"encryptedAesKey": aes_key_encoded,
"iv": iv_encoded,
"timestamp": timestamp,
"nonce": nonce,
"encryptedPayload": ciphertext_encoded,
"tag": tag_encoded,
}
request = Request() # 不使用缓存
response = request.post(
url="http://192.168.3.103:30380/",
headers={
"Authorization": "Bearer C52FB4D10BC424D9F",
"Content-Type": "application/json;charset=utf-8",
},
json=encrypt(
payload={
"productId": "BANK_CARD_4",
"name": "刘弼仁",
"idNumber": "131002198705020000",
"bankCard": "1234567890123456",
"phone": "18058798752",
}
),
)
print(response)

View File

@ -0,0 +1,3 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApafHKXu1IwGJ1iQVT0susJ20MRl/6unJ+YMX6Lx9l43/+THA4VAJ8YQaZMH9+1eFeaFjwM9pT0ee+okBpKRuDWVkH8NxiKza4ohNHEtSUU37N3h8BEUBJHx74/6pDK8DwPsp4LzGhnUPof5/ZMqW2phqTrEYRwYOKGLYNnScNKkOWkQMSrUxGBkcMs1VsePChe0cJqrC03CTHJ/NRSyc62HqDZLp6G+DaR3VgWkhvhT4sN/3j/n9Ml5io/8I5ooPfBrYPNCfOaVqFWtqHqoG0uMLeROa8edqk6ZBBv2mERPllOG43f91FNmg/kF8XHij1LBNcf1WbO7rHg0Vo71j+wIDAQAB
-----END PUBLIC KEY-----