huace-aigc-auth-client 1.1.21__py3-none-any.whl → 1.1.28__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.
- huace_aigc_auth_client/__init__.py +1 -1
- huace_aigc_auth_client/api_stats_collector.py +59 -12
- huace_aigc_auth_client/sdk.py +156 -56
- {huace_aigc_auth_client-1.1.21.dist-info → huace_aigc_auth_client-1.1.28.dist-info}/METADATA +1 -1
- {huace_aigc_auth_client-1.1.21.dist-info → huace_aigc_auth_client-1.1.28.dist-info}/RECORD +8 -8
- {huace_aigc_auth_client-1.1.21.dist-info → huace_aigc_auth_client-1.1.28.dist-info}/WHEEL +0 -0
- {huace_aigc_auth_client-1.1.21.dist-info → huace_aigc_auth_client-1.1.28.dist-info}/licenses/LICENSE +0 -0
- {huace_aigc_auth_client-1.1.21.dist-info → huace_aigc_auth_client-1.1.28.dist-info}/top_level.txt +0 -0
|
@@ -18,8 +18,7 @@ class ApiStatsCollector:
|
|
|
18
18
|
api_url: str,
|
|
19
19
|
app_id: str,
|
|
20
20
|
app_secret: str,
|
|
21
|
-
|
|
22
|
-
batch_size: int = 10,
|
|
21
|
+
batch_size: int = 50,
|
|
23
22
|
flush_interval: float = 5.0,
|
|
24
23
|
enabled: bool = True
|
|
25
24
|
):
|
|
@@ -30,7 +29,6 @@ class ApiStatsCollector:
|
|
|
30
29
|
api_url: 统计接口 URL(如:http://auth.example.com/api/sdk/stats/report/batch)
|
|
31
30
|
app_id: 应用 ID
|
|
32
31
|
app_secret: 应用密钥
|
|
33
|
-
token: 用户访问令牌
|
|
34
32
|
batch_size: 批量提交大小
|
|
35
33
|
flush_interval: 刷新间隔(秒)
|
|
36
34
|
enabled: 是否启用
|
|
@@ -38,7 +36,6 @@ class ApiStatsCollector:
|
|
|
38
36
|
self.api_url = api_url.rstrip('/')
|
|
39
37
|
self.app_id = app_id
|
|
40
38
|
self.app_secret = app_secret
|
|
41
|
-
self.token = token
|
|
42
39
|
self.batch_size = batch_size
|
|
43
40
|
self.flush_interval = flush_interval
|
|
44
41
|
self.enabled = enabled
|
|
@@ -69,6 +66,7 @@ class ApiStatsCollector:
|
|
|
69
66
|
api_method: str,
|
|
70
67
|
status_code: int,
|
|
71
68
|
response_time: float,
|
|
69
|
+
token: str,
|
|
72
70
|
error_message: Optional[str] = None,
|
|
73
71
|
request_params: Optional[Dict[str, Any]] = None
|
|
74
72
|
):
|
|
@@ -80,21 +78,26 @@ class ApiStatsCollector:
|
|
|
80
78
|
api_method: 请求方法
|
|
81
79
|
status_code: 状态码
|
|
82
80
|
response_time: 响应时间(秒)
|
|
81
|
+
token: 用户访问令牌
|
|
83
82
|
error_message: 错误信息
|
|
84
83
|
request_params: 请求参数(包含 headers, query_params, view_params, request_body, form_params)
|
|
85
84
|
"""
|
|
86
85
|
if not self.enabled:
|
|
87
86
|
return
|
|
88
87
|
|
|
88
|
+
# 过滤重定向请求(3xx 状态码),这些通常是框架自动处理的,不应统计
|
|
89
|
+
if 300 <= status_code < 400:
|
|
90
|
+
return
|
|
91
|
+
|
|
89
92
|
try:
|
|
90
93
|
stat_data = {
|
|
91
94
|
'api_path': api_path,
|
|
92
95
|
'api_method': api_method,
|
|
93
96
|
'status_code': status_code,
|
|
94
97
|
'response_time': response_time,
|
|
98
|
+
'token': token,
|
|
95
99
|
'error_message': error_message,
|
|
96
|
-
'request_params': request_params
|
|
97
|
-
'timestamp': datetime.utcnow().isoformat()
|
|
100
|
+
'request_params': request_params
|
|
98
101
|
}
|
|
99
102
|
self.queue.put_nowait(stat_data)
|
|
100
103
|
except queue.Full:
|
|
@@ -135,19 +138,50 @@ class ApiStatsCollector:
|
|
|
135
138
|
self._flush_buffer(buffer)
|
|
136
139
|
|
|
137
140
|
def _flush_buffer(self, buffer: List[Dict[str, Any]]):
|
|
138
|
-
"""
|
|
141
|
+
"""刷新缓冲区:按token分组批量提交统计数据"""
|
|
139
142
|
if not buffer:
|
|
140
143
|
return
|
|
141
144
|
|
|
145
|
+
try:
|
|
146
|
+
# 按token分组
|
|
147
|
+
token_groups: Dict[str, List[Dict[str, Any]]] = {}
|
|
148
|
+
for stat in buffer:
|
|
149
|
+
token = stat.get('token')
|
|
150
|
+
if not token:
|
|
151
|
+
continue
|
|
152
|
+
|
|
153
|
+
if token not in token_groups:
|
|
154
|
+
token_groups[token] = []
|
|
155
|
+
|
|
156
|
+
# 移除token字段后添加到分组
|
|
157
|
+
stat_copy = stat.copy()
|
|
158
|
+
stat_copy.pop('token', None)
|
|
159
|
+
token_groups[token].append(stat_copy)
|
|
160
|
+
|
|
161
|
+
# 对每个token分组分别提交
|
|
162
|
+
for token, stats in token_groups.items():
|
|
163
|
+
self._submit_stats(token, stats)
|
|
164
|
+
|
|
165
|
+
except Exception:
|
|
166
|
+
pass # 静默失败,不影响主流程
|
|
167
|
+
|
|
168
|
+
def _submit_stats(self, token: str, stats: List[Dict[str, Any]]):
|
|
169
|
+
"""
|
|
170
|
+
提交统计数据到服务端
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
token: 用户访问令牌
|
|
174
|
+
stats: 统计数据列表(已移除token字段)
|
|
175
|
+
"""
|
|
142
176
|
try:
|
|
143
177
|
headers = {
|
|
144
178
|
'X-App-Id': self.app_id,
|
|
145
179
|
'X-App-Secret': self.app_secret,
|
|
146
|
-
'Authorization': f'Bearer {
|
|
180
|
+
'Authorization': f'Bearer {token}',
|
|
147
181
|
'Content-Type': 'application/json'
|
|
148
182
|
}
|
|
149
183
|
|
|
150
|
-
payload = {'stats':
|
|
184
|
+
payload = {'stats': stats}
|
|
151
185
|
|
|
152
186
|
response = requests.post(
|
|
153
187
|
f'{self.api_url}/stats/report/batch',
|
|
@@ -173,7 +207,6 @@ def init_api_stats_collector(
|
|
|
173
207
|
api_url: str,
|
|
174
208
|
app_id: str,
|
|
175
209
|
app_secret: str,
|
|
176
|
-
token: str,
|
|
177
210
|
batch_size: int = 10,
|
|
178
211
|
flush_interval: float = 5.0,
|
|
179
212
|
enabled: bool = True
|
|
@@ -185,7 +218,6 @@ def init_api_stats_collector(
|
|
|
185
218
|
api_url: 统计接口 URL
|
|
186
219
|
app_id: 应用 ID
|
|
187
220
|
app_secret: 应用密钥
|
|
188
|
-
token: 用户访问令牌
|
|
189
221
|
batch_size: 批量提交大小
|
|
190
222
|
flush_interval: 刷新间隔(秒)
|
|
191
223
|
enabled: 是否启用
|
|
@@ -198,7 +230,6 @@ def init_api_stats_collector(
|
|
|
198
230
|
api_url=api_url,
|
|
199
231
|
app_id=app_id,
|
|
200
232
|
app_secret=app_secret,
|
|
201
|
-
token=token,
|
|
202
233
|
batch_size=batch_size,
|
|
203
234
|
flush_interval=flush_interval,
|
|
204
235
|
enabled=enabled
|
|
@@ -224,6 +255,7 @@ def collect_api_stat(
|
|
|
224
255
|
api_method: str,
|
|
225
256
|
status_code: int,
|
|
226
257
|
response_time: float,
|
|
258
|
+
token: str,
|
|
227
259
|
error_message: Optional[str] = None,
|
|
228
260
|
request_params: Optional[Dict[str, Any]] = None
|
|
229
261
|
):
|
|
@@ -231,7 +263,21 @@ def collect_api_stat(
|
|
|
231
263
|
快捷方法:收集接口统计数据
|
|
232
264
|
|
|
233
265
|
使用全局收集器实例
|
|
266
|
+
注意:会自动过滤 3xx 重定向状态码的请求
|
|
267
|
+
|
|
268
|
+
Args:
|
|
269
|
+
api_path: 接口路径
|
|
270
|
+
api_method: 请求方法
|
|
271
|
+
status_code: 状态码
|
|
272
|
+
response_time: 响应时间(秒)
|
|
273
|
+
token: 用户访问令牌
|
|
274
|
+
error_message: 错误信息
|
|
275
|
+
request_params: 请求参数
|
|
234
276
|
"""
|
|
277
|
+
# 过滤重定向请求(3xx 状态码),这些通常是框架自动处理的,不应统计
|
|
278
|
+
if 300 <= status_code < 400:
|
|
279
|
+
return
|
|
280
|
+
|
|
235
281
|
collector = get_api_stats_collector()
|
|
236
282
|
if collector:
|
|
237
283
|
collector.collect(
|
|
@@ -239,6 +285,7 @@ def collect_api_stat(
|
|
|
239
285
|
api_method=api_method,
|
|
240
286
|
status_code=status_code,
|
|
241
287
|
response_time=response_time,
|
|
288
|
+
token=token,
|
|
242
289
|
error_message=error_message,
|
|
243
290
|
request_params=request_params
|
|
244
291
|
)
|
huace_aigc_auth_client/sdk.py
CHANGED
|
@@ -570,24 +570,28 @@ class AuthMiddleware:
|
|
|
570
570
|
self.exclude_paths = exclude_paths or []
|
|
571
571
|
self.exclude_prefixes = exclude_prefixes or []
|
|
572
572
|
self.enable_stats = enable_stats
|
|
573
|
-
self.stats_collector = None
|
|
574
573
|
|
|
575
|
-
#
|
|
574
|
+
# 如果启用统计,初始化全局统计收集器
|
|
576
575
|
if self.enable_stats:
|
|
577
576
|
self.stats_api_url = stats_api_url or f"{self.client.base_url}/sdk"
|
|
577
|
+
self._init_global_stats_collector()
|
|
578
578
|
|
|
579
|
-
def
|
|
580
|
-
"""
|
|
581
|
-
if not self.enable_stats
|
|
579
|
+
def _init_global_stats_collector(self):
|
|
580
|
+
"""初始化全局统计收集器(只初始化一次)"""
|
|
581
|
+
if not self.enable_stats:
|
|
582
582
|
return
|
|
583
583
|
|
|
584
584
|
try:
|
|
585
|
-
from .api_stats_collector import init_api_stats_collector
|
|
586
|
-
|
|
585
|
+
from .api_stats_collector import init_api_stats_collector, get_api_stats_collector
|
|
586
|
+
|
|
587
|
+
# 检查是否已经初始化
|
|
588
|
+
if get_api_stats_collector() is not None:
|
|
589
|
+
return
|
|
590
|
+
|
|
591
|
+
init_api_stats_collector(
|
|
587
592
|
api_url=self.stats_api_url,
|
|
588
593
|
app_id=self.client.app_id,
|
|
589
594
|
app_secret=self.client.app_secret,
|
|
590
|
-
token=token,
|
|
591
595
|
batch_size=10,
|
|
592
596
|
flush_interval=5.0,
|
|
593
597
|
enabled=True
|
|
@@ -615,21 +619,31 @@ class AuthMiddleware:
|
|
|
615
619
|
"form_params": None
|
|
616
620
|
}
|
|
617
621
|
|
|
618
|
-
#
|
|
622
|
+
# 获取 content-type
|
|
623
|
+
content_type = request.headers.get('Content-Type', '').lower()
|
|
624
|
+
|
|
625
|
+
# 获取请求体(使用白名单方式,只处理已知安全的内容类型)
|
|
619
626
|
if request.is_json:
|
|
620
627
|
try:
|
|
621
628
|
params["request_body"] = request.get_json(silent=True)
|
|
622
629
|
except Exception:
|
|
623
630
|
pass
|
|
624
|
-
elif
|
|
631
|
+
elif 'application/x-www-form-urlencoded' in content_type:
|
|
632
|
+
# 表单数据
|
|
633
|
+
if request.form:
|
|
634
|
+
params["form_params"] = request.form.to_dict(flat=False)
|
|
635
|
+
elif 'multipart/form-data' in content_type:
|
|
636
|
+
# 文件上传请求,不读取 request.data,避免损坏文件流
|
|
637
|
+
# 只收集表单字段(非文件字段)
|
|
638
|
+
if request.form:
|
|
639
|
+
params["form_params"] = request.form.to_dict(flat=False)
|
|
640
|
+
elif content_type.startswith('text/') and request.data and len(request.data) < 10240:
|
|
641
|
+
# 只读取文本类型的请求体,且限制大小避免内存问题
|
|
625
642
|
try:
|
|
626
643
|
params["request_body"] = request.data.decode('utf-8')
|
|
627
644
|
except Exception:
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
# 获取表单数据
|
|
631
|
-
if request.form:
|
|
632
|
-
params["form_params"] = request.form.to_dict(flat=False)
|
|
645
|
+
pass
|
|
646
|
+
# 其他类型(如 application/octet-stream 等二进制流)直接跳过,不读取
|
|
633
647
|
|
|
634
648
|
return params
|
|
635
649
|
except Exception as e:
|
|
@@ -656,28 +670,32 @@ class AuthMiddleware:
|
|
|
656
670
|
"form_params": None
|
|
657
671
|
}
|
|
658
672
|
|
|
659
|
-
#
|
|
660
|
-
content_type = request.headers.get("content-type", "")
|
|
673
|
+
# 获取请求体(使用白名单方式,只处理已知安全的内容类型)
|
|
674
|
+
content_type = request.headers.get("content-type", "").lower()
|
|
675
|
+
# 读取请求体(需要特殊处理以避免消耗流)
|
|
676
|
+
body = b""
|
|
677
|
+
if request.method in ["POST", "PUT", "PATCH"]:
|
|
678
|
+
try:
|
|
679
|
+
body = await request.body()
|
|
680
|
+
except:
|
|
681
|
+
pass
|
|
682
|
+
params["request_body"] = body.decode('utf-8', errors='ignore')
|
|
661
683
|
|
|
662
684
|
if "application/json" in content_type:
|
|
663
685
|
try:
|
|
664
686
|
params["request_body"] = await request.json()
|
|
665
687
|
except Exception:
|
|
666
688
|
pass
|
|
667
|
-
elif "application/x-www-form-urlencoded" in content_type
|
|
689
|
+
elif "application/x-www-form-urlencoded" in content_type:
|
|
668
690
|
try:
|
|
669
691
|
form = await request.form()
|
|
670
692
|
params["form_params"] = {k: v for k, v in form.items()}
|
|
671
693
|
except Exception:
|
|
672
694
|
pass
|
|
673
|
-
|
|
674
|
-
#
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
if body:
|
|
678
|
-
params["request_body"] = body.decode('utf-8')
|
|
679
|
-
except Exception:
|
|
680
|
-
pass
|
|
695
|
+
elif "multipart/form-data" in content_type:
|
|
696
|
+
# 文件上传请求,不读取 body,避免损坏文件流
|
|
697
|
+
params["form_params"] = {"skipped_multipart": True}
|
|
698
|
+
# 其他类型(如 application/octet-stream 等二进制流)直接跳过,不读取
|
|
681
699
|
|
|
682
700
|
return params
|
|
683
701
|
except Exception as e:
|
|
@@ -690,22 +708,27 @@ class AuthMiddleware:
|
|
|
690
708
|
api_method: str,
|
|
691
709
|
status_code: int,
|
|
692
710
|
response_time: float,
|
|
711
|
+
token: str,
|
|
693
712
|
error_message: Optional[str] = None,
|
|
694
713
|
request_params: Optional[Dict[str, Any]] = None
|
|
695
714
|
):
|
|
696
715
|
"""收集接口统计"""
|
|
697
|
-
if not self.enable_stats
|
|
716
|
+
if not self.enable_stats:
|
|
698
717
|
return
|
|
699
718
|
|
|
700
719
|
try:
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
720
|
+
from .api_stats_collector import get_api_stats_collector
|
|
721
|
+
collector = get_api_stats_collector()
|
|
722
|
+
if collector:
|
|
723
|
+
collector.collect(
|
|
724
|
+
api_path=api_path,
|
|
725
|
+
api_method=api_method,
|
|
726
|
+
status_code=status_code,
|
|
727
|
+
response_time=response_time,
|
|
728
|
+
token=token,
|
|
729
|
+
error_message=error_message,
|
|
730
|
+
request_params=request_params
|
|
731
|
+
)
|
|
709
732
|
except Exception:
|
|
710
733
|
pass # 静默失败
|
|
711
734
|
|
|
@@ -726,9 +749,83 @@ class AuthMiddleware:
|
|
|
726
749
|
return None
|
|
727
750
|
return authorization[7:]
|
|
728
751
|
|
|
752
|
+
def get_fastapi_middleware_class(self, user_info_callback: Callable = None):
|
|
753
|
+
"""
|
|
754
|
+
获取 FastAPI 中间件类(推荐使用,避免请求体读取问题)
|
|
755
|
+
|
|
756
|
+
使用方法:
|
|
757
|
+
from starlette.middleware.base import BaseHTTPMiddleware
|
|
758
|
+
app.add_middleware(auth_middleware.get_fastapi_middleware_class())
|
|
759
|
+
|
|
760
|
+
Returns:
|
|
761
|
+
继承于 BaseHTTPMiddleware 的中间件类
|
|
762
|
+
"""
|
|
763
|
+
from fastapi.responses import JSONResponse
|
|
764
|
+
from starlette.middleware.base import BaseHTTPMiddleware
|
|
765
|
+
|
|
766
|
+
auth_middleware_instance = self
|
|
767
|
+
|
|
768
|
+
class AuthHTTPMiddleware(BaseHTTPMiddleware):
|
|
769
|
+
async def dispatch(self, request, call_next):
|
|
770
|
+
path = request.url.path
|
|
771
|
+
start_time = time.time()
|
|
772
|
+
|
|
773
|
+
# 收集请求参数
|
|
774
|
+
request_params = await auth_middleware_instance._collect_fastapi_request_params(request) if auth_middleware_instance.enable_stats else None
|
|
775
|
+
|
|
776
|
+
# 检查是否跳过
|
|
777
|
+
if auth_middleware_instance._should_skip(path):
|
|
778
|
+
return await call_next(request)
|
|
779
|
+
|
|
780
|
+
# 获取 Authorization header
|
|
781
|
+
authorization = request.headers.get("Authorization")
|
|
782
|
+
token = auth_middleware_instance._extract_token(authorization)
|
|
783
|
+
|
|
784
|
+
if not token:
|
|
785
|
+
logger.warning("AuthMiddleware未提供认证信息")
|
|
786
|
+
response_time = time.time() - start_time
|
|
787
|
+
auth_middleware_instance._collect_stats(path, request.method, 401, response_time, "", "未提供认证信息", request_params)
|
|
788
|
+
return JSONResponse(
|
|
789
|
+
status_code=401,
|
|
790
|
+
content={"code": 401, "message": "未提供认证信息", "data": None}
|
|
791
|
+
)
|
|
792
|
+
|
|
793
|
+
# 验证 token
|
|
794
|
+
try:
|
|
795
|
+
user_info = auth_middleware_instance.client.get_user_info(token)
|
|
796
|
+
# 将用户信息存储到 request.state
|
|
797
|
+
request.state.user_info = user_info
|
|
798
|
+
# 设置上下文
|
|
799
|
+
set_current_user(dataclasses.asdict(user_info))
|
|
800
|
+
if user_info_callback:
|
|
801
|
+
await user_info_callback(request, user_info)
|
|
802
|
+
except AigcAuthError as e:
|
|
803
|
+
logger.error(f"AuthMiddleware认证失败: {e.message}")
|
|
804
|
+
response_time = time.time() - start_time
|
|
805
|
+
auth_middleware_instance._collect_stats(path, request.method, 401, response_time, token, e.message, request_params)
|
|
806
|
+
return JSONResponse(
|
|
807
|
+
status_code=401,
|
|
808
|
+
content={"code": e.code, "message": e.message, "data": None}
|
|
809
|
+
)
|
|
810
|
+
|
|
811
|
+
# 处理请求
|
|
812
|
+
try:
|
|
813
|
+
response = await call_next(request)
|
|
814
|
+
response_time = time.time() - start_time
|
|
815
|
+
auth_middleware_instance._collect_stats(path, request.method, response.status_code, response_time, token, None, request_params)
|
|
816
|
+
return response
|
|
817
|
+
except Exception as e:
|
|
818
|
+
response_time = time.time() - start_time
|
|
819
|
+
auth_middleware_instance._collect_stats(path, request.method, 500, response_time, token, str(e), request_params)
|
|
820
|
+
raise
|
|
821
|
+
finally:
|
|
822
|
+
clear_current_user()
|
|
823
|
+
|
|
824
|
+
return AuthHTTPMiddleware
|
|
825
|
+
|
|
729
826
|
async def fastapi_middleware(self, request, call_next, user_info_callback: Callable = None):
|
|
730
827
|
"""
|
|
731
|
-
FastAPI
|
|
828
|
+
FastAPI 中间件(旧方法,推荐使用 get_fastapi_middleware_class)
|
|
732
829
|
|
|
733
830
|
使用方法:
|
|
734
831
|
@app.middleware("http")
|
|
@@ -736,12 +833,14 @@ class AuthMiddleware:
|
|
|
736
833
|
return await auth_middleware.fastapi_middleware(request, call_next)
|
|
737
834
|
"""
|
|
738
835
|
from fastapi.responses import JSONResponse
|
|
836
|
+
|
|
837
|
+
# 处理代理头部,确保重定向(如果有)使用正确的协议
|
|
838
|
+
forwarded_proto = request.headers.get("x-forwarded-proto")
|
|
839
|
+
if forwarded_proto:
|
|
840
|
+
request.scope["scheme"] = forwarded_proto
|
|
739
841
|
|
|
740
842
|
path = request.url.path
|
|
741
843
|
start_time = time.time()
|
|
742
|
-
|
|
743
|
-
# 收集请求参数
|
|
744
|
-
request_params = await self._collect_fastapi_request_params(request) if self.enable_stats else None
|
|
745
844
|
|
|
746
845
|
# 检查是否跳过
|
|
747
846
|
if self._should_skip(path):
|
|
@@ -754,7 +853,9 @@ class AuthMiddleware:
|
|
|
754
853
|
if not token:
|
|
755
854
|
logger.warning("AuthMiddleware未提供认证信息")
|
|
756
855
|
response_time = time.time() - start_time
|
|
757
|
-
|
|
856
|
+
# 收集请求参数
|
|
857
|
+
request_params = await self._collect_fastapi_request_params(request) if self.enable_stats else None
|
|
858
|
+
self._collect_stats(path, request.method, 401, response_time, "", "未提供认证信息", request_params)
|
|
758
859
|
return JSONResponse(
|
|
759
860
|
status_code=401,
|
|
760
861
|
content={"code": 401, "message": "未提供认证信息", "data": None}
|
|
@@ -763,24 +864,18 @@ class AuthMiddleware:
|
|
|
763
864
|
# 验证 token
|
|
764
865
|
try:
|
|
765
866
|
user_info = self.client.get_user_info(token)
|
|
766
|
-
# 初始化统计收集器(第一次有token时)
|
|
767
|
-
if self.enable_stats and self.stats_collector is None:
|
|
768
|
-
self._init_stats_collector(token)
|
|
769
867
|
# 将用户信息存储到 request.state
|
|
770
868
|
request.state.user_info = user_info
|
|
771
869
|
# 设置上下文
|
|
772
870
|
set_current_user(dataclasses.asdict(user_info))
|
|
773
|
-
|
|
774
|
-
# 处理代理头部,确保重定向(如果有)使用正确的协议
|
|
775
|
-
forwarded_proto = request.headers.get("x-forwarded-proto")
|
|
776
|
-
if forwarded_proto:
|
|
777
|
-
request.scope["scheme"] = forwarded_proto
|
|
778
871
|
if user_info_callback:
|
|
779
872
|
await user_info_callback(request, user_info)
|
|
780
873
|
except AigcAuthError as e:
|
|
781
874
|
logger.error(f"AuthMiddleware认证失败: {e.message}")
|
|
782
875
|
response_time = time.time() - start_time
|
|
783
|
-
|
|
876
|
+
# 收集请求参数
|
|
877
|
+
request_params = await self._collect_fastapi_request_params(request) if self.enable_stats else None
|
|
878
|
+
self._collect_stats(path, request.method, 401, response_time, token, e.message, request_params)
|
|
784
879
|
return JSONResponse(
|
|
785
880
|
status_code=401,
|
|
786
881
|
content={"code": e.code, "message": e.message, "data": None}
|
|
@@ -790,11 +885,15 @@ class AuthMiddleware:
|
|
|
790
885
|
try:
|
|
791
886
|
response = await call_next(request)
|
|
792
887
|
response_time = time.time() - start_time
|
|
793
|
-
|
|
888
|
+
# 收集请求参数
|
|
889
|
+
request_params = await self._collect_fastapi_request_params(request) if self.enable_stats else None
|
|
890
|
+
self._collect_stats(path, request.method, response.status_code, response_time, token, None, request_params)
|
|
794
891
|
return response
|
|
795
892
|
except Exception as e:
|
|
893
|
+
# 收集请求参数
|
|
894
|
+
request_params = await self._collect_fastapi_request_params(request) if self.enable_stats else None
|
|
796
895
|
response_time = time.time() - start_time
|
|
797
|
-
self._collect_stats(path, request.method, 500, response_time, str(e), request_params)
|
|
896
|
+
self._collect_stats(path, request.method, 500, response_time, token, str(e), request_params)
|
|
798
897
|
raise
|
|
799
898
|
finally:
|
|
800
899
|
clear_current_user()
|
|
@@ -828,7 +927,7 @@ class AuthMiddleware:
|
|
|
828
927
|
if not token:
|
|
829
928
|
logger.warning("AuthMiddleware未提供认证信息")
|
|
830
929
|
response_time = time.time() - g.start_time
|
|
831
|
-
self._collect_stats(path, request.method, 401, response_time, "未提供认证信息", g.request_params)
|
|
930
|
+
self._collect_stats(path, request.method, 401, response_time, "", "未提供认证信息", g.request_params)
|
|
832
931
|
return jsonify({
|
|
833
932
|
"code": 401,
|
|
834
933
|
"message": "未提供认证信息",
|
|
@@ -838,9 +937,6 @@ class AuthMiddleware:
|
|
|
838
937
|
# 验证 token
|
|
839
938
|
try:
|
|
840
939
|
user_info = self.client.get_user_info(token)
|
|
841
|
-
# 初始化统计收集器(第一次有token时)
|
|
842
|
-
if self.enable_stats and self.stats_collector is None:
|
|
843
|
-
self._init_stats_collector(token)
|
|
844
940
|
# 将用户信息存储到 flask.g
|
|
845
941
|
g.user_info = user_info
|
|
846
942
|
# 设置上下文
|
|
@@ -850,7 +946,7 @@ class AuthMiddleware:
|
|
|
850
946
|
except AigcAuthError as e:
|
|
851
947
|
logger.error(f"AuthMiddleware认证失败: {e.message}")
|
|
852
948
|
response_time = time.time() - g.start_time
|
|
853
|
-
self._collect_stats(path, request.method, 401, response_time, e.message, g.request_params)
|
|
949
|
+
self._collect_stats(path, request.method, 401, response_time, token, e.message, g.request_params)
|
|
854
950
|
return jsonify({
|
|
855
951
|
"code": e.code,
|
|
856
952
|
"message": e.message,
|
|
@@ -876,11 +972,15 @@ class AuthMiddleware:
|
|
|
876
972
|
if hasattr(g, 'start_time'):
|
|
877
973
|
response_time = time.time() - g.start_time
|
|
878
974
|
request_params = getattr(g, 'request_params', None)
|
|
975
|
+
# 从 request 的 Authorization header 获取 token
|
|
976
|
+
authorization = request.headers.get("Authorization")
|
|
977
|
+
token = self._extract_token(authorization) or ""
|
|
879
978
|
self._collect_stats(
|
|
880
979
|
request.path,
|
|
881
980
|
request.method,
|
|
882
981
|
response.status_code,
|
|
883
982
|
response_time,
|
|
983
|
+
token,
|
|
884
984
|
None,
|
|
885
985
|
request_params
|
|
886
986
|
)
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
huace_aigc_auth_client/__init__.py,sha256=
|
|
2
|
-
huace_aigc_auth_client/api_stats_collector.py,sha256=
|
|
1
|
+
huace_aigc_auth_client/__init__.py,sha256=nCpT58WU83NkEpdyMiq35t4Zd261cdp45N_07NRoisY,4630
|
|
2
|
+
huace_aigc_auth_client/api_stats_collector.py,sha256=UCUu2CHWsJyiAM58-XLQtfxWK0fjZ8TTdw90gv_Gx04,10347
|
|
3
3
|
huace_aigc_auth_client/legacy_adapter.py,sha256=TVCBAKejE2z2HQFsEwDW8LMiaIkXNfz3Mxv6_E-UJFY,24102
|
|
4
|
-
huace_aigc_auth_client/sdk.py,sha256=
|
|
4
|
+
huace_aigc_auth_client/sdk.py,sha256=49UrQnS96a_m-FM9KVSqmE8x4J-R4qpYsE4KgzasvFE,36742
|
|
5
5
|
huace_aigc_auth_client/user_context.py,sha256=KzevYLsLv1hv8rlvRw83FT-HugeoBJSJ1Pi56iLWyTE,5592
|
|
6
6
|
huace_aigc_auth_client/webhook.py,sha256=XQZYEbMoqIdqZWCGSTcedeDKJpDbUVSq5g08g-6Qucg,4124
|
|
7
7
|
huace_aigc_auth_client/webhook_flask.py,sha256=Iosu4dBtRhQZM_ytn-bn82MpVsyOiV28FBnt7Tfh31U,7225
|
|
8
|
-
huace_aigc_auth_client-1.1.
|
|
9
|
-
huace_aigc_auth_client-1.1.
|
|
10
|
-
huace_aigc_auth_client-1.1.
|
|
11
|
-
huace_aigc_auth_client-1.1.
|
|
12
|
-
huace_aigc_auth_client-1.1.
|
|
8
|
+
huace_aigc_auth_client-1.1.28.dist-info/licenses/LICENSE,sha256=z7dgC7KljhBLNvKjN15391nMj3aLt0gbud8-Yf1F8EQ,1063
|
|
9
|
+
huace_aigc_auth_client-1.1.28.dist-info/METADATA,sha256=9TkNkzidAbMP7nb2drl1FfSOAumh6ngs7ueiik40mnE,23629
|
|
10
|
+
huace_aigc_auth_client-1.1.28.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
11
|
+
huace_aigc_auth_client-1.1.28.dist-info/top_level.txt,sha256=kbv0nQ6PQ0JVneWPH7O2AbtlJnP7AjvFJ6JjM6ZEBxo,23
|
|
12
|
+
huace_aigc_auth_client-1.1.28.dist-info/RECORD,,
|
|
File without changes
|
{huace_aigc_auth_client-1.1.21.dist-info → huace_aigc_auth_client-1.1.28.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
{huace_aigc_auth_client-1.1.21.dist-info → huace_aigc_auth_client-1.1.28.dist-info}/top_level.txt
RENAMED
|
File without changes
|