gapi-requests-auth 0.1.0__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.
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Created by Ci Song @ Damo of Alibaba at 15:57 2026/2/3 using PyCharm
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
4
|
+
import hmac
|
|
5
|
+
import time
|
|
6
|
+
import uuid
|
|
7
|
+
from urllib.parse import urlsplit, parse_qs
|
|
8
|
+
|
|
9
|
+
import requests
|
|
10
|
+
from requests.auth import AuthBase
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _coalesce(x):
|
|
14
|
+
return "" if x is None else x
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _sha256_hex_bytes(b: bytes) -> str:
|
|
18
|
+
return hashlib.sha256(b).hexdigest()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class AKSKAuth(AuthBase):
|
|
22
|
+
|
|
23
|
+
def __init__(self, ak: str, sk: str, nonce_factory=None, ts_factory=None):
|
|
24
|
+
self.ak = ak
|
|
25
|
+
self.sk = sk
|
|
26
|
+
self.nonce_factory = nonce_factory or (lambda: uuid.uuid4().hex)
|
|
27
|
+
self.ts_factory = ts_factory or (lambda: str(int(time.time())))
|
|
28
|
+
|
|
29
|
+
def __call__(self, r: requests.PreparedRequest):
|
|
30
|
+
# ---- timestamp:优先用请求里已有的 x-request-ts,否则自动补上
|
|
31
|
+
timestamp = r.headers.get("x-request-ts")
|
|
32
|
+
if not timestamp:
|
|
33
|
+
timestamp = self.ts_factory()
|
|
34
|
+
r.headers["x-request-ts"] = timestamp
|
|
35
|
+
|
|
36
|
+
# ---- nonce:每次请求生成
|
|
37
|
+
nonce = self.nonce_factory()
|
|
38
|
+
|
|
39
|
+
# 放 header
|
|
40
|
+
r.headers.setdefault("x-request-ak", self.ak)
|
|
41
|
+
r.headers.setdefault("x-request-nonce", nonce)
|
|
42
|
+
|
|
43
|
+
# ---- path
|
|
44
|
+
u = urlsplit(r.url)
|
|
45
|
+
path = _coalesce(u.path)
|
|
46
|
+
|
|
47
|
+
# ---- parameters:取 query 参数,按 key 排序;同 key 多值用逗号拼接
|
|
48
|
+
params = parse_qs(u.query, keep_blank_values=True)
|
|
49
|
+
|
|
50
|
+
if not params:
|
|
51
|
+
parameter_string_repr = ""
|
|
52
|
+
else:
|
|
53
|
+
items = []
|
|
54
|
+
for k in sorted(params.keys()):
|
|
55
|
+
v_list = params.get(k) or [""]
|
|
56
|
+
v_joined = ",".join([str(v) for v in v_list])
|
|
57
|
+
items.append(f"{k}={v_joined}")
|
|
58
|
+
parameter_string_repr = "&".join(items)
|
|
59
|
+
|
|
60
|
+
# ---- bodyHash:sha256Hex(coalesce(body))
|
|
61
|
+
body = r.body
|
|
62
|
+
if body is None:
|
|
63
|
+
body_bytes = b""
|
|
64
|
+
elif isinstance(body, bytes):
|
|
65
|
+
body_bytes = body
|
|
66
|
+
else:
|
|
67
|
+
body_bytes = str(body).encode("utf-8")
|
|
68
|
+
|
|
69
|
+
body_hash = _sha256_hex_bytes(body_bytes)
|
|
70
|
+
|
|
71
|
+
# ---- method
|
|
72
|
+
method = (r.method or "").upper()
|
|
73
|
+
|
|
74
|
+
# ---- repr 拼接并整体 lower
|
|
75
|
+
repr_str = (
|
|
76
|
+
f"{path}\n"
|
|
77
|
+
f"{parameter_string_repr}\n"
|
|
78
|
+
f"{body_hash}\n"
|
|
79
|
+
f"{method}\n"
|
|
80
|
+
f"{self.ak}\n"
|
|
81
|
+
f"{nonce}\n"
|
|
82
|
+
f"{timestamp}\n"
|
|
83
|
+
f"{self.sk}"
|
|
84
|
+
).lower()
|
|
85
|
+
|
|
86
|
+
# ---- HMAC-SHA256(key=sk, msg=repr) 然后对 hmacResult 再 sha256Hex
|
|
87
|
+
hmac_bytes = hmac.new(self.sk.encode("utf-8"), repr_str.encode("utf-8"), hashlib.sha256).digest()
|
|
88
|
+
signature = hashlib.sha256(hmac_bytes).hexdigest()
|
|
89
|
+
|
|
90
|
+
r.headers["Authorization"] = "GAPI-SIMPLE-HMAC-SHA256 " + signature
|
|
91
|
+
|
|
92
|
+
return r
|
|
File without changes
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gapi-requests-auth
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Requests AuthBase for GAPI simple AK/SK HMAC signing
|
|
5
|
+
Author-email: 舞舟 <songci.sc@alibaba-inc.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Requires-Dist: requests>=2.31.0
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
|
|
11
|
+
# gapi-requests-auth
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
pip install gapi-requests-auth
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
```python
|
|
18
|
+
import requests
|
|
19
|
+
from gapi_requests_auth import AKSKAuth
|
|
20
|
+
|
|
21
|
+
s = requests.Session()
|
|
22
|
+
s.auth = AKSKAuth(ak="xxx", sk="yyy")
|
|
23
|
+
|
|
24
|
+
r = s.get("https://example.com/api?x=1")
|
|
25
|
+
print(r.status_code, r.text)
|
|
26
|
+
```
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
gapi_requests_auth/__init__.py,sha256=m5AdI1FSEE-qNKiZQBZ5GGkOPqlKSWumYpaw8zwlHN8,50
|
|
2
|
+
gapi_requests_auth/auth.py,sha256=2zjZdRT-WcSfc2PnznEItk7n4pCb_m-SmAWdiezUIt0,2789
|
|
3
|
+
gapi_requests_auth/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
gapi_requests_auth-0.1.0.dist-info/METADATA,sha256=_on4AJGPUtxOzX_tJKhAKAhvvgkogw0gj6sD7c3rmpI,559
|
|
5
|
+
gapi_requests_auth-0.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
6
|
+
gapi_requests_auth-0.1.0.dist-info/RECORD,,
|