pytest-dsl 0.1.1__py3-none-any.whl → 0.3.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.
Files changed (31) hide show
  1. pytest_dsl/cli.py +42 -6
  2. pytest_dsl/core/__init__.py +7 -0
  3. pytest_dsl/core/custom_keyword_manager.py +213 -0
  4. pytest_dsl/core/dsl_executor.py +171 -3
  5. pytest_dsl/core/http_request.py +163 -54
  6. pytest_dsl/core/lexer.py +36 -1
  7. pytest_dsl/core/parser.py +155 -13
  8. pytest_dsl/core/parsetab.py +82 -49
  9. pytest_dsl/core/variable_utils.py +1 -1
  10. pytest_dsl/examples/custom/test_advanced_keywords.auto +31 -0
  11. pytest_dsl/examples/custom/test_custom_keywords.auto +37 -0
  12. pytest_dsl/examples/custom/test_default_values.auto +34 -0
  13. pytest_dsl/examples/http/http_retry_assertions.auto +2 -2
  14. pytest_dsl/examples/http/http_retry_assertions_enhanced.auto +2 -2
  15. pytest_dsl/examples/test_custom_keyword.py +9 -0
  16. pytest_dsl/examples/test_http.py +0 -139
  17. pytest_dsl/keywords/http_keywords.py +290 -102
  18. pytest_dsl/parsetab.py +69 -0
  19. pytest_dsl-0.3.0.dist-info/METADATA +448 -0
  20. {pytest_dsl-0.1.1.dist-info → pytest_dsl-0.3.0.dist-info}/RECORD +24 -24
  21. {pytest_dsl-0.1.1.dist-info → pytest_dsl-0.3.0.dist-info}/WHEEL +1 -1
  22. pytest_dsl/core/custom_auth_example.py +0 -425
  23. pytest_dsl/examples/csrf_auth_provider.py +0 -232
  24. pytest_dsl/examples/http/csrf_auth_test.auto +0 -64
  25. pytest_dsl/examples/http/custom_auth_test.auto +0 -76
  26. pytest_dsl/examples/http_clients.yaml +0 -48
  27. pytest_dsl/examples/keyword_example.py +0 -70
  28. pytest_dsl-0.1.1.dist-info/METADATA +0 -504
  29. {pytest_dsl-0.1.1.dist-info → pytest_dsl-0.3.0.dist-info}/entry_points.txt +0 -0
  30. {pytest_dsl-0.1.1.dist-info → pytest_dsl-0.3.0.dist-info}/licenses/LICENSE +0 -0
  31. {pytest_dsl-0.1.1.dist-info → pytest_dsl-0.3.0.dist-info}/top_level.txt +0 -0
@@ -1,425 +0,0 @@
1
- """自定义授权示例模块
2
-
3
- 该模块提供了几个复杂授权流程的实现示例,展示如何开发和注册自定义授权提供者。
4
- """
5
-
6
- import time
7
- import json
8
- import logging
9
- import hashlib
10
- import hmac
11
- import base64
12
- import uuid
13
- from datetime import datetime
14
- from typing import Dict, Any, Optional
15
-
16
- import requests
17
-
18
- from pytest_dsl.core.auth_provider import (
19
- AuthProvider, register_auth_provider, CustomAuthProvider
20
- )
21
-
22
- logger = logging.getLogger(__name__)
23
-
24
-
25
- class HmacAuthProvider(AuthProvider):
26
- """HMAC签名认证提供者
27
-
28
- 使用HMAC算法对请求参数进行签名,常用于AWS, 阿里云等API认证。
29
- """
30
-
31
- def __init__(self, access_key: str, secret_key: str,
32
- region: str = None, service: str = None,
33
- algorithm: str = 'sha256'):
34
- """初始化HMAC签名认证
35
-
36
- Args:
37
- access_key: 访问密钥ID
38
- secret_key: 秘密访问密钥
39
- region: 区域 (用于某些云服务API)
40
- service: 服务名称 (用于某些云服务API)
41
- algorithm: 哈希算法 ('sha256', 'sha1', 等)
42
- """
43
- self.access_key = access_key
44
- self.secret_key = secret_key
45
- self.region = region
46
- self.service = service
47
- self.algorithm = algorithm
48
-
49
- def apply_auth(self, request_kwargs: Dict[str, Any]) -> Dict[str, Any]:
50
- """应用HMAC签名认证
51
-
52
- Args:
53
- request_kwargs: 请求参数
54
-
55
- Returns:
56
- 更新后的请求参数
57
- """
58
- # 确保headers存在
59
- if "headers" not in request_kwargs:
60
- request_kwargs["headers"] = {}
61
-
62
- # 获取当前时间
63
- now = datetime.utcnow()
64
- amz_date = now.strftime('%Y%m%dT%H%M%SZ')
65
- date_stamp = now.strftime('%Y%m%d')
66
-
67
- # 添加必要的头部
68
- request_kwargs["headers"]["X-Amz-Date"] = amz_date
69
-
70
- # 准备签名参数
71
- method = request_kwargs.get("method", "GET")
72
- url_path = "/" # 简化示例
73
-
74
- # 创建规范请求
75
- canonical_uri = url_path
76
- canonical_querystring = ""
77
-
78
- # 创建规范头部
79
- canonical_headers = '\n'.join([
80
- f"{header.lower()}:{value}"
81
- for header, value in sorted(request_kwargs["headers"].items())
82
- ]) + '\n'
83
-
84
- signed_headers = ';'.join([
85
- header.lower() for header in sorted(request_kwargs["headers"].keys())
86
- ])
87
-
88
- # 获取请求体的哈希值
89
- payload_hash = hashlib.sha256(b'').hexdigest() # 简化示例
90
-
91
- # 创建规范请求
92
- canonical_request = f"{method}\n{canonical_uri}\n{canonical_querystring}\n{canonical_headers}\n{signed_headers}\n{payload_hash}"
93
-
94
- # 创建签名字符串
95
- algorithm = f"HMAC-{self.algorithm.upper()}"
96
- credential_scope = f"{date_stamp}/{self.region}/{self.service}/aws4_request"
97
- string_to_sign = f"{algorithm}\n{amz_date}\n{credential_scope}\n{hashlib.sha256(canonical_request.encode('utf-8')).hexdigest()}"
98
-
99
- # 计算签名
100
- k_date = hmac.new(f"AWS4{self.secret_key}".encode('utf-8'), date_stamp.encode('utf-8'), hashlib.sha256).digest()
101
- k_region = hmac.new(k_date, self.region.encode('utf-8'), hashlib.sha256).digest()
102
- k_service = hmac.new(k_region, self.service.encode('utf-8'), hashlib.sha256).digest()
103
- k_signing = hmac.new(k_service, b"aws4_request", hashlib.sha256).digest()
104
- signature = hmac.new(k_signing, string_to_sign.encode('utf-8'), hashlib.sha256).hexdigest()
105
-
106
- # 添加授权头
107
- authorization_header = (
108
- f"{algorithm} "
109
- f"Credential={self.access_key}/{credential_scope}, "
110
- f"SignedHeaders={signed_headers}, "
111
- f"Signature={signature}"
112
- )
113
- request_kwargs["headers"]["Authorization"] = authorization_header
114
-
115
- return request_kwargs
116
-
117
-
118
- class JwtRefreshAuthProvider(AuthProvider):
119
- """JWT刷新令牌授权提供者
120
-
121
- 使用刷新令牌自动获取和刷新JWT访问令牌。
122
- """
123
-
124
- def __init__(self, token_url: str, refresh_token: str, client_id: str = None,
125
- client_secret: str = None, token_refresh_window: int = 60):
126
- """初始化JWT刷新令牌授权
127
-
128
- Args:
129
- token_url: 获取/刷新令牌的URL
130
- refresh_token: 刷新令牌
131
- client_id: 客户端ID (可选)
132
- client_secret: 客户端密钥 (可选)
133
- token_refresh_window: 令牌刷新窗口 (秒)
134
- """
135
- self.token_url = token_url
136
- self.refresh_token = refresh_token
137
- self.client_id = client_id
138
- self.client_secret = client_secret
139
- self.token_refresh_window = token_refresh_window
140
-
141
- # 令牌缓存
142
- self._access_token = None
143
- self._token_expires_at = 0
144
-
145
- def apply_auth(self, request_kwargs: Dict[str, Any]) -> Dict[str, Any]:
146
- """应用JWT授权
147
-
148
- Args:
149
- request_kwargs: 请求参数
150
-
151
- Returns:
152
- 更新后的请求参数
153
- """
154
- # 确保有有效的令牌
155
- self._ensure_valid_token()
156
-
157
- # 确保headers存在
158
- if "headers" not in request_kwargs:
159
- request_kwargs["headers"] = {}
160
-
161
- # 添加认证头
162
- request_kwargs["headers"]["Authorization"] = f"Bearer {self._access_token}"
163
- return request_kwargs
164
-
165
- def _ensure_valid_token(self) -> None:
166
- """确保有有效的访问令牌"""
167
- current_time = time.time()
168
-
169
- # 如果令牌不存在或即将过期,刷新令牌
170
- if not self._access_token or current_time + self.token_refresh_window >= self._token_expires_at:
171
- self._refresh_token()
172
-
173
- def _refresh_token(self) -> None:
174
- """使用刷新令牌获取新的访问令牌"""
175
- data = {
176
- "grant_type": "refresh_token",
177
- "refresh_token": self.refresh_token
178
- }
179
-
180
- # 添加客户端凭据(如果提供)
181
- headers = {}
182
- if self.client_id and self.client_secret:
183
- auth_header = base64.b64encode(f"{self.client_id}:{self.client_secret}".encode('utf-8')).decode('utf-8')
184
- headers["Authorization"] = f"Basic {auth_header}"
185
-
186
- try:
187
- response = requests.post(self.token_url, data=data, headers=headers)
188
- response.raise_for_status()
189
-
190
- token_data = response.json()
191
- self._access_token = token_data.get("access_token")
192
- expires_in = token_data.get("expires_in", 3600) # 默认1小时
193
-
194
- # 如果有新的刷新令牌,更新它
195
- if "refresh_token" in token_data:
196
- self.refresh_token = token_data["refresh_token"]
197
-
198
- if not self._access_token:
199
- raise ValueError("响应中缺少access_token字段")
200
-
201
- # 计算过期时间
202
- self._token_expires_at = time.time() + expires_in
203
- logger.info(f"成功刷新JWT令牌,有效期{expires_in}秒")
204
-
205
- except Exception as e:
206
- logger.error(f"刷新JWT令牌失败: {str(e)}")
207
- raise
208
-
209
-
210
- class WechatMiniAppAuthProvider(AuthProvider):
211
- """微信小程序认证提供者
212
-
213
- 使用code获取微信小程序的session_key和openid,适用于微信小程序API调用。
214
- """
215
-
216
- def __init__(self, appid: str, secret: str, code: str = None):
217
- """初始化微信小程序认证
218
-
219
- Args:
220
- appid: 小程序的AppID
221
- secret: 小程序的AppSecret
222
- code: 登录时获取的临时code
223
- """
224
- self.appid = appid
225
- self.secret = secret
226
- self.code = code
227
-
228
- # 微信登录凭据
229
- self.session_key = None
230
- self.openid = None
231
-
232
- def apply_auth(self, request_kwargs: Dict[str, Any]) -> Dict[str, Any]:
233
- """应用微信小程序认证
234
-
235
- Args:
236
- request_kwargs: 请求参数
237
-
238
- Returns:
239
- 更新后的请求参数
240
- """
241
- # 如果未获取过登录凭据且有code,则先获取凭据
242
- if (not self.session_key or not self.openid) and self.code:
243
- self._login_with_code()
244
-
245
- # 确保已获取登录凭据
246
- if not self.session_key or not self.openid:
247
- raise ValueError("未获取到微信登录凭据,无法完成认证")
248
-
249
- # 确保headers存在
250
- if "headers" not in request_kwargs:
251
- request_kwargs["headers"] = {}
252
-
253
- # 添加认证头
254
- request_kwargs["headers"]["X-WX-OPENID"] = self.openid
255
- request_kwargs["headers"]["X-WX-SESSION-KEY"] = self.session_key
256
-
257
- return request_kwargs
258
-
259
- def _login_with_code(self) -> None:
260
- """使用code获取登录凭据"""
261
- url = f"https://api.weixin.qq.com/sns/jscode2session"
262
- params = {
263
- "appid": self.appid,
264
- "secret": self.secret,
265
- "js_code": self.code,
266
- "grant_type": "authorization_code"
267
- }
268
-
269
- try:
270
- response = requests.get(url, params=params)
271
- response.raise_for_status()
272
-
273
- data = response.json()
274
- if "errcode" in data and data["errcode"] != 0:
275
- raise ValueError(f"微信登录失败: {data.get('errmsg', '未知错误')}")
276
-
277
- self.session_key = data.get("session_key")
278
- self.openid = data.get("openid")
279
-
280
- if not self.session_key or not self.openid:
281
- raise ValueError("微信登录响应缺少session_key或openid")
282
-
283
- logger.info(f"成功获取微信小程序登录凭据,openid: {self.openid}")
284
-
285
- # 清除code,因为它是一次性的
286
- self.code = None
287
-
288
- except Exception as e:
289
- logger.error(f"获取微信小程序登录凭据失败: {str(e)}")
290
- raise
291
-
292
-
293
- class MultiStepAuthProvider(CustomAuthProvider):
294
- """多步骤认证提供者
295
-
296
- 实现一个需要多个步骤的认证流程,包括获取临时令牌、换取访问令牌等。
297
- """
298
-
299
- def __init__(self):
300
- """初始化多步骤认证提供者"""
301
- self._cached_token = None
302
- self._token_expires_at = 0
303
-
304
- def apply_auth(self, request_kwargs: Dict[str, Any]) -> Dict[str, Any]:
305
- """应用多步骤认证
306
-
307
- Args:
308
- request_kwargs: 请求参数
309
-
310
- Returns:
311
- 更新后的请求参数
312
- """
313
- # 检查缓存令牌是否有效
314
- current_time = time.time()
315
- cached_token = self._cached_token
316
-
317
- if cached_token and current_time < self._token_expires_at:
318
- # 令牌有效,直接使用
319
- if "headers" not in request_kwargs:
320
- request_kwargs["headers"] = {}
321
- request_kwargs["headers"]["Authorization"] = f"Bearer {cached_token}"
322
- return request_kwargs
323
-
324
- # 令牌无效,开始多步骤认证流程
325
- try:
326
- # 步骤1: 获取临时令牌
327
- temp_token_response = requests.post(
328
- "https://api.example.com/auth/temp-token",
329
- json={
330
- "app_id": "your_app_id",
331
- "app_secret": "your_app_secret",
332
- "device_id": str(uuid.uuid4()) # 示例设备ID
333
- }
334
- )
335
- temp_token_response.raise_for_status()
336
- temp_token_data = temp_token_response.json()
337
- temp_token = temp_token_data.get("temp_token")
338
-
339
- if not temp_token:
340
- raise ValueError("无法获取临时令牌")
341
-
342
- # 步骤2: 用临时令牌换取访问令牌
343
- access_token_response = requests.post(
344
- "https://api.example.com/auth/access-token",
345
- json={
346
- "temp_token": temp_token,
347
- "client_id": "your_client_id",
348
- "signature": hashlib.sha256(f"your_client_id:{temp_token}:your_client_secret".encode()).hexdigest()
349
- }
350
- )
351
- access_token_response.raise_for_status()
352
- token_data = access_token_response.json()
353
-
354
- # 提取数据
355
- access_token = token_data.get("access_token")
356
- expires_in = token_data.get("expires_in", 3600)
357
-
358
- if not access_token:
359
- raise ValueError("无法获取访问令牌")
360
-
361
- # 缓存令牌
362
- self._cached_token = access_token
363
- self._token_expires_at = time.time() + expires_in - 60 # 提前60秒刷新
364
-
365
- # 步骤3: 应用访问令牌到请求
366
- if "headers" not in request_kwargs:
367
- request_kwargs["headers"] = {}
368
- request_kwargs["headers"]["Authorization"] = f"Bearer {access_token}"
369
-
370
- return request_kwargs
371
-
372
- except Exception as e:
373
- logger.error(f"多步骤认证失败: {str(e)}")
374
- # 出错时,尝试使用之前的令牌(即使已过期)
375
- if cached_token:
376
- if "headers" not in request_kwargs:
377
- request_kwargs["headers"] = {}
378
- request_kwargs["headers"]["Authorization"] = f"Bearer {cached_token}"
379
- logger.warning("使用过期的缓存令牌")
380
-
381
- return request_kwargs
382
-
383
-
384
- def register_complex_auth_example():
385
- """注册复杂授权示例"""
386
- # 注册HMAC签名认证提供者
387
- register_auth_provider(
388
- "hmac_aws_auth",
389
- HmacAuthProvider,
390
- access_key="AKIAIOSFODNN7EXAMPLE",
391
- secret_key="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
392
- region="us-west-2",
393
- service="s3",
394
- algorithm="sha256"
395
- )
396
-
397
- # 注册JWT刷新令牌认证提供者
398
- register_auth_provider(
399
- "jwt_refresh_auth",
400
- JwtRefreshAuthProvider,
401
- token_url="https://api.example.com/oauth/token",
402
- refresh_token="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
403
- client_id="client_id_example",
404
- client_secret="client_secret_example"
405
- )
406
-
407
- # 注册微信小程序认证提供者
408
- register_auth_provider(
409
- "wechat_miniapp_auth",
410
- WechatMiniAppAuthProvider,
411
- appid="wx1234567890abcdef",
412
- secret="abcdef1234567890abcdef1234567890",
413
- code="023Bj3ll2EE81B0TrTnl2kQSll2Bj3lY"
414
- )
415
-
416
- # 注册多步骤认证提供者
417
- register_auth_provider(
418
- "multi_step_auth",
419
- MultiStepAuthProvider
420
- )
421
-
422
- logger.info("已注册复杂授权示例")
423
-
424
- # 自动注册示例(取消注释以启用)
425
- # register_complex_auth_example()
@@ -1,232 +0,0 @@
1
- """CSRF认证提供者示例
2
-
3
- 此模块提供了一个CSRF认证提供者的示例实现,展示如何使用AuthProvider的响应处理钩子。
4
- """
5
-
6
- import logging
7
- import json
8
- import re
9
- from typing import Dict, Any
10
- import requests
11
- from pytest_dsl.core.auth_provider import CustomAuthProvider, register_auth_provider
12
-
13
- logger = logging.getLogger(__name__)
14
-
15
-
16
- class CSRFAuthProvider(CustomAuthProvider):
17
- """CSRF令牌认证提供者
18
-
19
- 此提供者从响应中提取CSRF令牌,并将其应用到后续请求中。
20
- 支持从Cookie、响应头或响应体中提取令牌。
21
- """
22
-
23
- def __init__(self,
24
- token_source: str = "header", # header, cookie, body
25
- source_name: str = "X-CSRF-Token", # 头名称、Cookie名称或JSON路径
26
- header_name: str = "X-CSRF-Token", # 请求头名称
27
- regex_pattern: str = None): # 从响应体提取的正则表达式
28
- """初始化CSRF令牌认证提供者
29
-
30
- Args:
31
- token_source: 令牌来源,可以是 "header"、"cookie" 或 "body"
32
- source_name: 源名称,取决于token_source:
33
- - 当token_source为"header"时,表示头名称
34
- - 当token_source为"cookie"时,表示Cookie名称
35
- - 当token_source为"body"时,表示JSON路径或CSS选择器
36
- header_name: 请求头名称,用于发送令牌
37
- regex_pattern: 从响应体提取令牌的正则表达式(当token_source为"body"时)
38
- """
39
- super().__init__()
40
- self.token_source = token_source
41
- self.source_name = source_name
42
- self.header_name = header_name
43
- self.regex_pattern = regex_pattern
44
- self.csrf_token = None
45
- self._session = requests.Session()
46
-
47
- # 标识此提供者管理会话
48
- self.manage_session = True
49
-
50
- logger.info(f"初始化CSRF令牌认证提供者 (源: {token_source}, 名称: {source_name})")
51
-
52
- def apply_auth(self, request_kwargs: Dict[str, Any]) -> Dict[str, Any]:
53
- """应用CSRF令牌认证
54
-
55
- 将CSRF令牌添加到请求头中。
56
-
57
- Args:
58
- request_kwargs: 请求参数
59
-
60
- Returns:
61
- 更新后的请求参数
62
- """
63
- # 确保headers存在
64
- if "headers" not in request_kwargs:
65
- request_kwargs["headers"] = {}
66
-
67
- # 如果已有令牌,添加到请求头
68
- if self.csrf_token:
69
- request_kwargs["headers"][self.header_name] = self.csrf_token
70
- logger.debug(f"添加CSRF令牌到请求头: {self.header_name}={self.csrf_token}")
71
- else:
72
- logger.warning("尚未获取到CSRF令牌,无法添加到请求头")
73
-
74
- return request_kwargs
75
-
76
- def process_response(self, response: requests.Response) -> None:
77
- """处理响应以提取CSRF令牌
78
-
79
- 从响应的头、Cookie或正文中提取CSRF令牌。
80
-
81
- Args:
82
- response: 响应对象
83
- """
84
- token = None
85
-
86
- # 从头中提取
87
- if self.token_source == "header":
88
- token = response.headers.get(self.source_name)
89
- if token:
90
- logger.debug(f"从响应头提取CSRF令牌: {self.source_name}={token}")
91
- else:
92
- logger.debug(f"响应头中未找到CSRF令牌: {self.source_name}")
93
-
94
- # 从Cookie中提取
95
- elif self.token_source == "cookie":
96
- token = response.cookies.get(self.source_name)
97
- if token:
98
- logger.debug(f"从Cookie提取CSRF令牌: {self.source_name}={token}")
99
- else:
100
- logger.debug(f"Cookie中未找到CSRF令牌: {self.source_name}")
101
-
102
- # 从响应体中提取
103
- elif self.token_source == "body":
104
- # 如果有正则表达式模式
105
- if self.regex_pattern:
106
- try:
107
- match = re.search(self.regex_pattern, response.text)
108
- if match and match.group(1):
109
- token = match.group(1)
110
- logger.debug(f"使用正则表达式从响应体提取CSRF令牌: {token}")
111
- else:
112
- logger.debug(f"正则表达式未匹配到CSRF令牌: {self.regex_pattern}")
113
- except Exception as e:
114
- logger.error(f"使用正则表达式提取CSRF令牌失败: {str(e)}")
115
- # 如果是JSON响应,尝试使用JSON路径
116
- elif 'application/json' in response.headers.get('Content-Type', ''):
117
- try:
118
- json_data = response.json()
119
-
120
- # 简单的点路径解析
121
- parts = self.source_name.strip('$').strip('.').split('.')
122
- data = json_data
123
- for part in parts:
124
- if part in data:
125
- data = data[part]
126
- else:
127
- data = None
128
- break
129
-
130
- if data and isinstance(data, str):
131
- token = data
132
- logger.debug(f"从JSON响应提取CSRF令牌: {self.source_name}={token}")
133
- else:
134
- logger.debug(f"JSON路径未找到CSRF令牌或值不是字符串: {self.source_name}")
135
-
136
- except Exception as e:
137
- logger.error(f"从JSON响应提取CSRF令牌失败: {str(e)}")
138
- # 如果是HTML响应,可以尝试使用CSS选择器或XPath
139
- else:
140
- logger.debug("响应不是JSON格式,无法使用JSON路径提取CSRF令牌")
141
- # 这里可以添加HTML解析逻辑,例如使用Beautiful Soup或lxml
142
-
143
- # 更新令牌
144
- if token:
145
- logger.info(f"成功提取CSRF令牌: {token}")
146
- self.csrf_token = token
147
-
148
- def clean_auth_state(self, request_kwargs: Dict[str, Any] = None) -> Dict[str, Any]:
149
- """清理认证状态
150
-
151
- 清理CSRF认证状态,包括令牌和会话。
152
-
153
- Args:
154
- request_kwargs: 请求参数
155
-
156
- Returns:
157
- 更新后的请求参数
158
- """
159
- logger.info("清理CSRF认证状态")
160
-
161
- # 重置CSRF令牌
162
- self.csrf_token = None
163
-
164
- # 清理会话Cookie
165
- self._session.cookies.clear()
166
-
167
- # 处理请求参数
168
- if request_kwargs:
169
- if "headers" in request_kwargs:
170
- # 移除CSRF相关头
171
- csrf_headers = [self.header_name, 'X-CSRF-Token', 'csrf-token', 'CSRF-Token']
172
- for header in csrf_headers:
173
- if header in request_kwargs["headers"]:
174
- request_kwargs["headers"].pop(header)
175
- logger.debug(f"已移除请求头: {header}")
176
-
177
- return request_kwargs if request_kwargs else {}
178
-
179
-
180
- # 使用示例
181
- def register_csrf_auth_providers():
182
- """注册CSRF认证提供者实例"""
183
-
184
- # 从头中提取CSRF令牌
185
- register_auth_provider(
186
- "csrf_header_auth",
187
- CSRFAuthProvider,
188
- token_source="header",
189
- source_name="X-CSRF-Token",
190
- header_name="X-CSRF-Token"
191
- )
192
-
193
- # 从Cookie中提取CSRF令牌
194
- register_auth_provider(
195
- "csrf_cookie_auth",
196
- CSRFAuthProvider,
197
- token_source="cookie",
198
- source_name="csrf_token",
199
- header_name="X-CSRF-Token"
200
- )
201
-
202
- # 从JSON响应体中提取CSRF令牌
203
- register_auth_provider(
204
- "csrf_json_auth",
205
- CSRFAuthProvider,
206
- token_source="body",
207
- source_name="data.security.csrf_token",
208
- header_name="X-CSRF-Token"
209
- )
210
-
211
- # 使用正则表达式从HTML响应体中提取CSRF令牌
212
- register_auth_provider(
213
- "csrf_html_auth",
214
- CSRFAuthProvider,
215
- token_source="body",
216
- header_name="X-CSRF-Token",
217
- regex_pattern=r'<meta name="csrf-token" content="([^"]+)">'
218
- )
219
-
220
- logger.info("已注册CSRF认证提供者")
221
-
222
-
223
- # 如果此模块被直接运行,注册CSRF认证提供者
224
- if __name__ == "__main__":
225
- # 配置日志
226
- logging.basicConfig(
227
- level=logging.DEBUG,
228
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
229
- )
230
-
231
- # 注册提供者
232
- register_csrf_auth_providers()