huace-aigc-auth-client 1.1.7__tar.gz → 1.1.9__tar.gz

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 (17) hide show
  1. {huace_aigc_auth_client-1.1.7/huace_aigc_auth_client.egg-info → huace_aigc_auth_client-1.1.9}/PKG-INFO +69 -97
  2. {huace_aigc_auth_client-1.1.7 → huace_aigc_auth_client-1.1.9}/README.md +68 -96
  3. {huace_aigc_auth_client-1.1.7 → huace_aigc_auth_client-1.1.9}/huace_aigc_auth_client/__init__.py +10 -2
  4. {huace_aigc_auth_client-1.1.7 → huace_aigc_auth_client-1.1.9}/huace_aigc_auth_client/legacy_adapter.py +25 -25
  5. huace_aigc_auth_client-1.1.9/huace_aigc_auth_client/webhook_flask.py +218 -0
  6. {huace_aigc_auth_client-1.1.7 → huace_aigc_auth_client-1.1.9/huace_aigc_auth_client.egg-info}/PKG-INFO +69 -97
  7. {huace_aigc_auth_client-1.1.7 → huace_aigc_auth_client-1.1.9}/huace_aigc_auth_client.egg-info/SOURCES.txt +1 -0
  8. {huace_aigc_auth_client-1.1.7 → huace_aigc_auth_client-1.1.9}/pyproject.toml +1 -1
  9. {huace_aigc_auth_client-1.1.7 → huace_aigc_auth_client-1.1.9}/LICENSE +0 -0
  10. {huace_aigc_auth_client-1.1.7 → huace_aigc_auth_client-1.1.9}/MANIFEST.in +0 -0
  11. {huace_aigc_auth_client-1.1.7 → huace_aigc_auth_client-1.1.9}/QUICK_START.txt +0 -0
  12. {huace_aigc_auth_client-1.1.7 → huace_aigc_auth_client-1.1.9}/huace_aigc_auth_client/sdk.py +0 -0
  13. {huace_aigc_auth_client-1.1.7 → huace_aigc_auth_client-1.1.9}/huace_aigc_auth_client/webhook.py +0 -0
  14. {huace_aigc_auth_client-1.1.7 → huace_aigc_auth_client-1.1.9}/huace_aigc_auth_client.egg-info/dependency_links.txt +0 -0
  15. {huace_aigc_auth_client-1.1.7 → huace_aigc_auth_client-1.1.9}/huace_aigc_auth_client.egg-info/requires.txt +0 -0
  16. {huace_aigc_auth_client-1.1.7 → huace_aigc_auth_client-1.1.9}/huace_aigc_auth_client.egg-info/top_level.txt +0 -0
  17. {huace_aigc_auth_client-1.1.7 → huace_aigc_auth_client-1.1.9}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: huace-aigc-auth-client
3
- Version: 1.1.7
3
+ Version: 1.1.9
4
4
  Summary: 华策AIGC Auth Client - 提供 Token 验证、用户信息获取、权限检查、旧系统接入等功能
5
5
  Author-email: Huace <support@huace.com>
6
6
  License: MIT
@@ -44,8 +44,7 @@ pip install huace-aigc-auth-client
44
44
  # 必填:应用 ID 和密钥(在鉴权中心创建应用后获取)
45
45
  AIGC_AUTH_APP_ID=your_app_id
46
46
  AIGC_AUTH_APP_SECRET=your_app_secret
47
-
48
- # 可选:鉴权服务地址(默认为生产环境)- 测试环境鉴权地址:https://auth-test.aigc.huacemedia.com/aigc-auth/api/v1
47
+ # 鉴权服务地址(默认为生产环境)- 测试环境鉴权地址:http://auth.aigc-test.huacemedia.com/aigc-auth/api/v1
49
48
  AIGC_AUTH_BASE_URL=https://aigc-auth.huacemedia.com/aigc-auth/api/v1
50
49
  ```
51
50
 
@@ -420,8 +419,8 @@ from huace_aigc_auth_client import LegacySystemAdapter, LegacyUserData
420
419
  class MyLegacyAdapter(LegacySystemAdapter):
421
420
  """实现与旧系统用户表的交互"""
422
421
 
423
- def __init__(self, db, sync_config):
424
- super().__init__(sync_config)
422
+ def __init__(self, db, sync_config, auth_client=None):
423
+ super().__init__(sync_config, auth_client)
425
424
  self.db = db
426
425
 
427
426
  def get_user_by_unique_field(self, username: str):
@@ -435,8 +434,23 @@ class MyLegacyAdapter(LegacySystemAdapter):
435
434
  "email": user.email,
436
435
  # ... 其他字段
437
436
  })
437
+
438
+ async def get_user_by_username_async(self, username: str) -> Optional[LegacyUserData]:
439
+ """异步通过用户名获取旧系统用户"""
440
+ user = await self.db.execute(
441
+ select(User).where(User.username == username)
442
+ )
443
+ user = user.scalars().first()
444
+ if not user:
445
+ return None
446
+ return LegacyUserData({
447
+ "id": user.id,
448
+ "username": user.username,
449
+ "email": user.email,
450
+ # ... 其他字段
451
+ })
438
452
 
439
- def create_user(self, user_data: dict):
453
+ async def _create_user_async(self, user_data: Dict[str, Any]) -> Optional[Any]:
440
454
  """在旧系统创建用户"""
441
455
  user = User(
442
456
  username=user_data["username"],
@@ -445,20 +459,39 @@ class MyLegacyAdapter(LegacySystemAdapter):
445
459
  # ... 其他字段
446
460
  )
447
461
  self.db.add(user)
448
- self.db.commit()
462
+ await self.db.commit()
449
463
  return user.id
450
464
 
451
- def update_user(self, username: str, user_data: dict):
452
- """更新旧系统用户"""
453
- user = self.db.query(User).filter(User.username == username).first()
465
+ async def _update_user_async(self, username: str, user_data: Dict[str, Any]) -> bool:
466
+ """异步更新旧系统用户"""
467
+ user = await self.db.execute(
468
+ select(User).where(User.username == username)
469
+ )
470
+ user = user.scalars().first()
454
471
  if user:
455
472
  for key, value in user_data.items():
456
473
  setattr(user, key, value)
457
- self.db.commit()
474
+ await self.db.commit()
475
+ return True
476
+ return False
477
+
478
+ async def _delete_user_async(self, username: str) -> bool:
479
+ """异步删除旧系统用户"""
480
+ user = await self.db.execute(
481
+ select(User).where(User.username == username)
482
+ )
483
+ user = user.scalars().first()
484
+ if user:
485
+ await self.db.delete(user)
486
+ # 或者软删除:user.is_active = False
487
+ await self.db.commit()
488
+ return True
489
+ return False
458
490
 
459
- def get_all_users(self):
460
- """获取所有用户(用于初始化同步)"""
461
- users = self.db.query(User).all()
491
+ async def get_all_users_async(self) -> List[LegacyUserData]:
492
+ """异步获取所有用户(用于初始化同步)"""
493
+ result = await self.db.execute(select(User))
494
+ users = result.scalars().all()
462
495
  return [LegacyUserData({"username": u.username, ...}) for u in users]
463
496
  ```
464
497
 
@@ -468,7 +501,7 @@ class MyLegacyAdapter(LegacySystemAdapter):
468
501
  from huace_aigc_auth_client import AigcAuthClient, UserSyncService
469
502
 
470
503
  client = AigcAuthClient()
471
- adapter = MyLegacyAdapter(db, sync_config)
504
+ adapter = MyLegacyAdapter(db, sync_config, client)
472
505
  sync_service = UserSyncService(client, adapter)
473
506
 
474
507
  # 在获取用户信息后调用同步
@@ -479,7 +512,7 @@ async def auth_middleware(request, call_next):
479
512
  # 登录成功后,同步用户到旧系统
480
513
  if hasattr(request.state, "user_info"):
481
514
  user_info = request.state.user_info
482
- sync_service.sync_on_login(user_info)
515
+ await sync_service.sync_on_login_async(user_info)
483
516
 
484
517
  return response
485
518
  ```
@@ -493,47 +526,35 @@ from fastapi import APIRouter
493
526
  from huace_aigc_auth_client import register_webhook_router
494
527
 
495
528
  api_router = APIRouter()
529
+ client = AigcAuthClient()
530
+ adapter = MyLegacyAdapter(db, sync_config, client)
496
531
 
497
532
  # 定义 webhook 处理函数
498
- async def handle_webhook(data: dict) -> dict:
533
+ async def handle_auth_webhook(request_data: Dict[str, Any]) -> Dict[str, Any]:
499
534
  """
500
- 处理来自 aigc-auth webhook 通知
535
+ 独立的 webhook 处理函数(用于 SDK 集成)
501
536
 
502
- Args:
503
- data: webhook 数据,包含 event 和 data 字段
504
-
505
- Returns:
506
- dict: 处理结果
537
+ 这个函数不需要 db 参数,会在内部创建数据库会话。
538
+ 适用于通过 SDK register_webhook_router 注册。
507
539
  """
508
- from your_app.db import get_db_session
540
+ from app.db.session import get_db
509
541
 
510
- event = data.get("event")
511
- user_data = data.get("data", {})
512
-
513
- # 创建数据库会话
514
- async with get_db_session() as db:
515
- adapter = MyLegacyAdapter(db, sync_config)
516
-
517
- if event == "user.created":
518
- # 处理用户创建事件
519
- if not adapter.get_user_by_unique_field(user_data["username"]):
520
- legacy_data = adapter.transform_auth_to_legacy(user_data)
521
- legacy_data["password"] = sync_config.unified_password
522
- adapter.create_user(legacy_data)
523
- await db.commit()
524
-
525
- elif event == "user.updated":
526
- # 处理用户更新事件
527
- legacy_data = adapter.transform_auth_to_legacy(user_data)
528
- adapter.update_user(user_data["username"], legacy_data)
529
- await db.commit()
530
-
531
- return {"status": "ok", "event": event}
542
+ async for db in get_db():
543
+ try:
544
+ event = request_data.get("event")
545
+ data = request_data.get("data", {})
546
+ logger.info(f"Received webhook event: {event} with data: {data}")
547
+
548
+ # 调用适配器的 handle_webhook 方法
549
+ return await adapter.handle_webhook(event, data)
550
+ except Exception as e:
551
+ logger.exception(f"Failed to handle webhook: {e}")
552
+ raise
532
553
 
533
554
  # 注册 webhook 路由(自动处理签名验证)
534
555
  register_webhook_router(
535
556
  api_router,
536
- handler=handle_webhook,
557
+ handler=handle_auth_webhook,
537
558
  prefix="/webhook", # 可选,默认 "/webhook"
538
559
  secret_env_key="aigc-auth-webhook-secret", # 可选,在 auth 后台配置
539
560
  tags=["Webhook"] # 可选,默认 ["Webhook"]
@@ -542,55 +563,6 @@ register_webhook_router(
542
563
  # webhook 端点将自动创建在: /webhook/auth
543
564
  ```
544
565
 
545
- **传统方式:手动实现 Webhook 端点**
546
-
547
- ```python
548
- import hmac
549
- import hashlib
550
- import os
551
-
552
- @app.post("/api/v1/webhook/auth")
553
- async def receive_auth_webhook(request: Request, db: Session = Depends(get_db)):
554
- """接收 aigc-auth 的用户变更通知"""
555
- # 获取原始请求体
556
- body = await request.body()
557
-
558
- # 验证签名
559
- signature = request.headers.get("X-Webhook-Signature", "")
560
- secret = os.getenv("AIGC_AUTH_WEBHOOK_SECRET", "")
561
-
562
- if secret:
563
- expected = hmac.new(
564
- secret.encode('utf-8'),
565
- body,
566
- hashlib.sha256
567
- ).hexdigest()
568
-
569
- if not hmac.compare_digest(expected, signature):
570
- raise HTTPException(status_code=401, detail="Invalid signature")
571
-
572
- # 解析数据
573
- data = await request.json()
574
- event = data.get("event")
575
- user_data = data.get("data", {})
576
-
577
- if event == "user.created":
578
- adapter = MyLegacyAdapter(db, sync_config)
579
- if not adapter.get_user_by_unique_field(user_data["username"]):
580
- legacy_data = adapter.transform_auth_to_legacy(user_data)
581
- legacy_data["password"] = sync_config.unified_password
582
- adapter.create_user(legacy_data)
583
- db.commit()
584
-
585
- elif event == "user.updated":
586
- adapter = MyLegacyAdapter(db, sync_config)
587
- legacy_data = adapter.transform_auth_to_legacy(user_data)
588
- adapter.update_user(user_data["username"], legacy_data)
589
- db.commit()
590
-
591
- return {"success": True}
592
- ```
593
-
594
566
  **Webhook 签名验证说明**
595
567
 
596
568
  SDK 的 `register_webhook_router` 会自动处理签名验证:
@@ -604,7 +576,7 @@ SDK 的 `register_webhook_router` 会自动处理签名验证:
604
576
  ```python
605
577
  # 一次性脚本:将旧系统用户同步到 aigc-auth
606
578
  async def init_sync():
607
- adapter = MyLegacyAdapter(db, sync_config)
579
+ adapter = MyLegacyAdapter(db, sync_config, client)
608
580
  users = adapter.get_all_users()
609
581
 
610
582
  for user in users:
@@ -19,8 +19,7 @@ pip install huace-aigc-auth-client
19
19
  # 必填:应用 ID 和密钥(在鉴权中心创建应用后获取)
20
20
  AIGC_AUTH_APP_ID=your_app_id
21
21
  AIGC_AUTH_APP_SECRET=your_app_secret
22
-
23
- # 可选:鉴权服务地址(默认为生产环境)- 测试环境鉴权地址:https://auth-test.aigc.huacemedia.com/aigc-auth/api/v1
22
+ # 鉴权服务地址(默认为生产环境)- 测试环境鉴权地址:http://auth.aigc-test.huacemedia.com/aigc-auth/api/v1
24
23
  AIGC_AUTH_BASE_URL=https://aigc-auth.huacemedia.com/aigc-auth/api/v1
25
24
  ```
26
25
 
@@ -395,8 +394,8 @@ from huace_aigc_auth_client import LegacySystemAdapter, LegacyUserData
395
394
  class MyLegacyAdapter(LegacySystemAdapter):
396
395
  """实现与旧系统用户表的交互"""
397
396
 
398
- def __init__(self, db, sync_config):
399
- super().__init__(sync_config)
397
+ def __init__(self, db, sync_config, auth_client=None):
398
+ super().__init__(sync_config, auth_client)
400
399
  self.db = db
401
400
 
402
401
  def get_user_by_unique_field(self, username: str):
@@ -410,8 +409,23 @@ class MyLegacyAdapter(LegacySystemAdapter):
410
409
  "email": user.email,
411
410
  # ... 其他字段
412
411
  })
412
+
413
+ async def get_user_by_username_async(self, username: str) -> Optional[LegacyUserData]:
414
+ """异步通过用户名获取旧系统用户"""
415
+ user = await self.db.execute(
416
+ select(User).where(User.username == username)
417
+ )
418
+ user = user.scalars().first()
419
+ if not user:
420
+ return None
421
+ return LegacyUserData({
422
+ "id": user.id,
423
+ "username": user.username,
424
+ "email": user.email,
425
+ # ... 其他字段
426
+ })
413
427
 
414
- def create_user(self, user_data: dict):
428
+ async def _create_user_async(self, user_data: Dict[str, Any]) -> Optional[Any]:
415
429
  """在旧系统创建用户"""
416
430
  user = User(
417
431
  username=user_data["username"],
@@ -420,20 +434,39 @@ class MyLegacyAdapter(LegacySystemAdapter):
420
434
  # ... 其他字段
421
435
  )
422
436
  self.db.add(user)
423
- self.db.commit()
437
+ await self.db.commit()
424
438
  return user.id
425
439
 
426
- def update_user(self, username: str, user_data: dict):
427
- """更新旧系统用户"""
428
- user = self.db.query(User).filter(User.username == username).first()
440
+ async def _update_user_async(self, username: str, user_data: Dict[str, Any]) -> bool:
441
+ """异步更新旧系统用户"""
442
+ user = await self.db.execute(
443
+ select(User).where(User.username == username)
444
+ )
445
+ user = user.scalars().first()
429
446
  if user:
430
447
  for key, value in user_data.items():
431
448
  setattr(user, key, value)
432
- self.db.commit()
449
+ await self.db.commit()
450
+ return True
451
+ return False
452
+
453
+ async def _delete_user_async(self, username: str) -> bool:
454
+ """异步删除旧系统用户"""
455
+ user = await self.db.execute(
456
+ select(User).where(User.username == username)
457
+ )
458
+ user = user.scalars().first()
459
+ if user:
460
+ await self.db.delete(user)
461
+ # 或者软删除:user.is_active = False
462
+ await self.db.commit()
463
+ return True
464
+ return False
433
465
 
434
- def get_all_users(self):
435
- """获取所有用户(用于初始化同步)"""
436
- users = self.db.query(User).all()
466
+ async def get_all_users_async(self) -> List[LegacyUserData]:
467
+ """异步获取所有用户(用于初始化同步)"""
468
+ result = await self.db.execute(select(User))
469
+ users = result.scalars().all()
437
470
  return [LegacyUserData({"username": u.username, ...}) for u in users]
438
471
  ```
439
472
 
@@ -443,7 +476,7 @@ class MyLegacyAdapter(LegacySystemAdapter):
443
476
  from huace_aigc_auth_client import AigcAuthClient, UserSyncService
444
477
 
445
478
  client = AigcAuthClient()
446
- adapter = MyLegacyAdapter(db, sync_config)
479
+ adapter = MyLegacyAdapter(db, sync_config, client)
447
480
  sync_service = UserSyncService(client, adapter)
448
481
 
449
482
  # 在获取用户信息后调用同步
@@ -454,7 +487,7 @@ async def auth_middleware(request, call_next):
454
487
  # 登录成功后,同步用户到旧系统
455
488
  if hasattr(request.state, "user_info"):
456
489
  user_info = request.state.user_info
457
- sync_service.sync_on_login(user_info)
490
+ await sync_service.sync_on_login_async(user_info)
458
491
 
459
492
  return response
460
493
  ```
@@ -468,47 +501,35 @@ from fastapi import APIRouter
468
501
  from huace_aigc_auth_client import register_webhook_router
469
502
 
470
503
  api_router = APIRouter()
504
+ client = AigcAuthClient()
505
+ adapter = MyLegacyAdapter(db, sync_config, client)
471
506
 
472
507
  # 定义 webhook 处理函数
473
- async def handle_webhook(data: dict) -> dict:
508
+ async def handle_auth_webhook(request_data: Dict[str, Any]) -> Dict[str, Any]:
474
509
  """
475
- 处理来自 aigc-auth webhook 通知
510
+ 独立的 webhook 处理函数(用于 SDK 集成)
476
511
 
477
- Args:
478
- data: webhook 数据,包含 event 和 data 字段
479
-
480
- Returns:
481
- dict: 处理结果
512
+ 这个函数不需要 db 参数,会在内部创建数据库会话。
513
+ 适用于通过 SDK register_webhook_router 注册。
482
514
  """
483
- from your_app.db import get_db_session
515
+ from app.db.session import get_db
484
516
 
485
- event = data.get("event")
486
- user_data = data.get("data", {})
487
-
488
- # 创建数据库会话
489
- async with get_db_session() as db:
490
- adapter = MyLegacyAdapter(db, sync_config)
491
-
492
- if event == "user.created":
493
- # 处理用户创建事件
494
- if not adapter.get_user_by_unique_field(user_data["username"]):
495
- legacy_data = adapter.transform_auth_to_legacy(user_data)
496
- legacy_data["password"] = sync_config.unified_password
497
- adapter.create_user(legacy_data)
498
- await db.commit()
499
-
500
- elif event == "user.updated":
501
- # 处理用户更新事件
502
- legacy_data = adapter.transform_auth_to_legacy(user_data)
503
- adapter.update_user(user_data["username"], legacy_data)
504
- await db.commit()
505
-
506
- return {"status": "ok", "event": event}
517
+ async for db in get_db():
518
+ try:
519
+ event = request_data.get("event")
520
+ data = request_data.get("data", {})
521
+ logger.info(f"Received webhook event: {event} with data: {data}")
522
+
523
+ # 调用适配器的 handle_webhook 方法
524
+ return await adapter.handle_webhook(event, data)
525
+ except Exception as e:
526
+ logger.exception(f"Failed to handle webhook: {e}")
527
+ raise
507
528
 
508
529
  # 注册 webhook 路由(自动处理签名验证)
509
530
  register_webhook_router(
510
531
  api_router,
511
- handler=handle_webhook,
532
+ handler=handle_auth_webhook,
512
533
  prefix="/webhook", # 可选,默认 "/webhook"
513
534
  secret_env_key="aigc-auth-webhook-secret", # 可选,在 auth 后台配置
514
535
  tags=["Webhook"] # 可选,默认 ["Webhook"]
@@ -517,55 +538,6 @@ register_webhook_router(
517
538
  # webhook 端点将自动创建在: /webhook/auth
518
539
  ```
519
540
 
520
- **传统方式:手动实现 Webhook 端点**
521
-
522
- ```python
523
- import hmac
524
- import hashlib
525
- import os
526
-
527
- @app.post("/api/v1/webhook/auth")
528
- async def receive_auth_webhook(request: Request, db: Session = Depends(get_db)):
529
- """接收 aigc-auth 的用户变更通知"""
530
- # 获取原始请求体
531
- body = await request.body()
532
-
533
- # 验证签名
534
- signature = request.headers.get("X-Webhook-Signature", "")
535
- secret = os.getenv("AIGC_AUTH_WEBHOOK_SECRET", "")
536
-
537
- if secret:
538
- expected = hmac.new(
539
- secret.encode('utf-8'),
540
- body,
541
- hashlib.sha256
542
- ).hexdigest()
543
-
544
- if not hmac.compare_digest(expected, signature):
545
- raise HTTPException(status_code=401, detail="Invalid signature")
546
-
547
- # 解析数据
548
- data = await request.json()
549
- event = data.get("event")
550
- user_data = data.get("data", {})
551
-
552
- if event == "user.created":
553
- adapter = MyLegacyAdapter(db, sync_config)
554
- if not adapter.get_user_by_unique_field(user_data["username"]):
555
- legacy_data = adapter.transform_auth_to_legacy(user_data)
556
- legacy_data["password"] = sync_config.unified_password
557
- adapter.create_user(legacy_data)
558
- db.commit()
559
-
560
- elif event == "user.updated":
561
- adapter = MyLegacyAdapter(db, sync_config)
562
- legacy_data = adapter.transform_auth_to_legacy(user_data)
563
- adapter.update_user(user_data["username"], legacy_data)
564
- db.commit()
565
-
566
- return {"success": True}
567
- ```
568
-
569
541
  **Webhook 签名验证说明**
570
542
 
571
543
  SDK 的 `register_webhook_router` 会自动处理签名验证:
@@ -579,7 +551,7 @@ SDK 的 `register_webhook_router` 会自动处理签名验证:
579
551
  ```python
580
552
  # 一次性脚本:将旧系统用户同步到 aigc-auth
581
553
  async def init_sync():
582
- adapter = MyLegacyAdapter(db, sync_config)
554
+ adapter = MyLegacyAdapter(db, sync_config, client)
583
555
  users = adapter.get_all_users()
584
556
 
585
557
  for user in users:
@@ -73,6 +73,11 @@ from .webhook import (
73
73
  verify_webhook_signature,
74
74
  )
75
75
 
76
+ from .webhook_flask import (
77
+ create_flask_webhook_blueprint,
78
+ register_flask_webhook_routes,
79
+ )
80
+
76
81
  __all__ = [
77
82
  # 核心类
78
83
  "AigcAuthClient",
@@ -94,8 +99,11 @@ __all__ = [
94
99
  "SyncResult",
95
100
  "create_sync_config",
96
101
  "create_default_field_mappings",
97
- # Webhook 接收
102
+ # Webhook 接收 (FastAPI)
98
103
  "register_webhook_router",
99
104
  "verify_webhook_signature",
105
+ # Webhook 接收 (Flask)
106
+ "create_flask_webhook_blueprint",
107
+ "register_flask_webhook_routes",
100
108
  ]
101
- __version__ = "1.1.7"
109
+ __version__ = "1.1.9"
@@ -101,11 +101,11 @@ class LegacySystemAdapter(ABC):
101
101
  旧系统适配器抽象基类
102
102
 
103
103
  接入系统需要继承此类并实现以下方法:
104
- - get_user_by_unique_field: 通过唯一字段获取用户
105
- - create_user: 创建用户
106
- - update_user: 更新用户(可选)
107
- - get_all_users: 获取所有用户(用于初始化同步)
108
- - handle_webhook: 处理 webhook 通知(可选,异步方法)
104
+ - get_user_by_username_async
105
+ - _create_user_async
106
+ - _update_user_async
107
+ - _delete_user_async
108
+ - get_all_users_async
109
109
  """
110
110
 
111
111
  def __init__(self, sync_config: SyncConfig, auth_client=None):
@@ -119,24 +119,6 @@ class LegacySystemAdapter(ABC):
119
119
  self.config = sync_config
120
120
  self.auth_client = auth_client
121
121
 
122
- @abstractmethod
123
- def get_user_by_unique_field(self, value: Any) -> Optional[LegacyUserData]:
124
- """通过唯一字段获取旧系统用户"""
125
- pass
126
-
127
- @abstractmethod
128
- def create_user(self, user_data: Dict[str, Any]) -> Optional[Any]:
129
- """在旧系统创建用户,返回用户ID"""
130
- pass
131
-
132
- def update_user(self, unique_value: Any, user_data: Dict[str, Any]) -> bool:
133
- """更新旧系统用户(可选实现)"""
134
- return False
135
-
136
- def get_all_users(self) -> List[LegacyUserData]:
137
- """获取所有旧系统用户(用于初始化同步)"""
138
- return []
139
-
140
122
  @abstractmethod
141
123
  async def get_user_by_username_async(self, username: str) -> Optional[LegacyUserData]:
142
124
  """异步获取用户(子类必须实现)
@@ -147,6 +129,7 @@ class LegacySystemAdapter(ABC):
147
129
  Returns:
148
130
  Optional[LegacyUserData]: 用户数据,不存在返回 None
149
131
  """
132
+ logger.info(f"Fetching user by username asynchronously: {username}")
150
133
  raise NotImplementedError("Subclass must implement get_user_by_username_async method")
151
134
 
152
135
  @abstractmethod
@@ -159,6 +142,7 @@ class LegacySystemAdapter(ABC):
159
142
  Returns:
160
143
  Optional[Any]: 创建的用户 ID 或其他标识
161
144
  """
145
+ logger.info(f"Creating user with data: {user_data}")
162
146
  raise NotImplementedError("Subclass must implement _create_user_async method")
163
147
 
164
148
  @abstractmethod
@@ -172,6 +156,7 @@ class LegacySystemAdapter(ABC):
172
156
  Returns:
173
157
  bool: 更新成功返回 True
174
158
  """
159
+ logger.info(f"Updating user: {username} with data: {user_data}")
175
160
  raise NotImplementedError("Subclass must implement _update_user_async method")
176
161
 
177
162
  @abstractmethod
@@ -184,6 +169,7 @@ class LegacySystemAdapter(ABC):
184
169
  Returns:
185
170
  bool: 删除成功返回 True
186
171
  """
172
+ logger.info(f"Deleting user: {username}")
187
173
  raise NotImplementedError("Subclass must implement _delete_user_async method")
188
174
 
189
175
  @abstractmethod
@@ -193,6 +179,7 @@ class LegacySystemAdapter(ABC):
193
179
  Returns:
194
180
  List[LegacyUserData]: 所有用户数据列表
195
181
  """
182
+ logger.info("Fetching all users from legacy system asynchronously")
196
183
  raise NotImplementedError("Subclass must implement get_all_users_async method")
197
184
 
198
185
  async def upsert_user_async(self, user_data: Dict[str, Any], auth_data: Dict[str, Any] = None) -> Dict[str, Any]:
@@ -208,6 +195,7 @@ class LegacySystemAdapter(ABC):
208
195
  """
209
196
  username = user_data.get("username")
210
197
  if not username:
198
+ logger.error("username is required for upsert operation")
211
199
  raise ValueError("username is required for upsert operation")
212
200
 
213
201
  # 检查用户是否存在
@@ -216,6 +204,7 @@ class LegacySystemAdapter(ABC):
216
204
  if existing:
217
205
  # 用户存在,执行更新
218
206
  if not auth_data or auth_data.get("updatedFields") is None or len(auth_data.get("updatedFields")) == 0:
207
+ logger.info(f"No updatedFields provided for user: {username}, skipping update")
219
208
  # 如果没有提供 auth_data 或 updatedFields,则不更新
220
209
  return {"created": False, "user_id": existing.get("id")}
221
210
  await self._update_user_async(username, user_data)
@@ -234,8 +223,10 @@ class LegacySystemAdapter(ABC):
234
223
  Dict: 同步结果统计
235
224
  """
236
225
  if not self.auth_client:
226
+ logger.error("auth_client is required for batch_sync_to_auth")
237
227
  raise ValueError("auth_client is required for batch_sync_to_auth. Please provide it in constructor.")
238
228
 
229
+ logger.info("Starting batch sync from legacy system to aigc-auth")
239
230
  users = await self.get_all_users_async()
240
231
 
241
232
  results = {
@@ -264,6 +255,7 @@ class LegacySystemAdapter(ABC):
264
255
  auth_data["password"] = password
265
256
 
266
257
  result = self.auth_client.sync_user_to_auth(auth_data)
258
+ logger.info(f"Sync result for user {user.get('username')}: {result}")
267
259
 
268
260
  if result.get("success"):
269
261
  if result.get("created"):
@@ -277,6 +269,7 @@ class LegacySystemAdapter(ABC):
277
269
  "error": result.get("message")
278
270
  })
279
271
  except Exception as e:
272
+ logger.error(f"Error syncing user {user.get('username')}: {e}")
280
273
  results["failed"] += 1
281
274
  results["errors"].append({
282
275
  "user": user.get("username"),
@@ -298,6 +291,7 @@ class LegacySystemAdapter(ABC):
298
291
  Returns:
299
292
  Dict: 处理结果
300
293
  """
294
+ logger.info(f"Handling webhook event: {event} with data: {data}")
301
295
  if event == "user.created" or event == "user.updated" or event == "user.login":
302
296
  # 转换数据格式
303
297
  legacy_data = self.transform_auth_to_legacy(data)
@@ -311,6 +305,7 @@ class LegacySystemAdapter(ABC):
311
305
  legacy_data["password"] = password
312
306
 
313
307
  # 创建或更新用户
308
+ logger.info(f"Handling {event} event for user: {legacy_data}")
314
309
  result = await self.upsert_user_async(legacy_data)
315
310
 
316
311
  return {
@@ -321,18 +316,23 @@ class LegacySystemAdapter(ABC):
321
316
  }
322
317
 
323
318
  elif event == "user.deleted":
319
+ logger.info("Handling user.deleted event")
324
320
  # 禁用用户而不是删除
325
321
  username = data.get("username")
326
322
  if not username:
323
+ logger.error("username is required for user.deleted event")
327
324
  logger.error("username is required for user.deleted event")
328
325
  return {"success": False, "message": "username is required for user.deleted event"}
326
+ logger.info(f"Disabling user: {username}")
329
327
  await self._delete_user_async(username)
330
328
 
331
329
  return {"success": True, "message": "User disabled"}
332
330
 
333
331
  elif event == "user.init_sync_auth":
332
+ logger.info("Handling user.init_sync_auth event")
334
333
  # 初始化同步:批量同步旧系统用户到 aigc-auth
335
334
  if not self.auth_client:
335
+ logger.error("auth_client is required for init_sync_auth event")
336
336
  return {"success": False, "message": "auth_client is required for init_sync_auth event. Please provide it in constructor."}
337
337
 
338
338
  results = await self.batch_sync_to_auth()
@@ -362,7 +362,7 @@ class LegacySystemAdapter(ABC):
362
362
 
363
363
  if auth_value is not None:
364
364
  result[mapping.legacy_field] = auth_value
365
-
365
+ logger.info(f"Transformed auth user({auth_user}) to legacy format: {result}")
366
366
  return result
367
367
 
368
368
  def transform_legacy_to_auth(self, legacy_user: LegacyUserData) -> Dict[str, Any]:
@@ -382,7 +382,7 @@ class LegacySystemAdapter(ABC):
382
382
 
383
383
  if legacy_value is not None:
384
384
  result[mapping.auth_field] = legacy_value
385
-
385
+ logger.info(f"Transformed legacy user({legacy_user}) to auth format: {result}")
386
386
  return result
387
387
 
388
388
  def get_password_for_sync(self, legacy_user: Optional[LegacyUserData] = None) -> tuple:
@@ -0,0 +1,218 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Flask Webhook 接口
4
+
5
+ 提供 Flask 版本的 webhook 接收功能,用于接收 aigc-auth 的用户变更通知。
6
+ 适用于使用 Flask 框架的项目。
7
+ """
8
+
9
+ import os
10
+ import hmac
11
+ import hashlib
12
+ import logging
13
+ from typing import Callable, Dict, Any, Optional
14
+ from flask import Blueprint, request, jsonify
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ def verify_webhook_signature(payload: bytes, signature: str, secret: str) -> bool:
20
+ """
21
+ 验证 webhook 签名
22
+
23
+ Args:
24
+ payload: 请求体(bytes)
25
+ signature: 签名字符串
26
+ secret: 密钥
27
+
28
+ Returns:
29
+ bool: 签名是否有效
30
+ """
31
+ if not secret:
32
+ # 如果未配置密钥,跳过验证(开发环境)
33
+ logger.warning("Webhook secret not configured, skipping signature verification")
34
+ return True
35
+
36
+ if not signature:
37
+ return False
38
+
39
+ expected = hmac.new(
40
+ secret.encode('utf-8'),
41
+ payload,
42
+ hashlib.sha256
43
+ ).hexdigest()
44
+
45
+ return hmac.compare_digest(expected, signature)
46
+
47
+
48
+ def create_flask_webhook_blueprint(
49
+ handler: Callable[[Dict[str, Any]], Dict[str, Any]],
50
+ secret_env_key: str = "AIGC_AUTH_WEBHOOK_SECRET",
51
+ url_prefix: str = "/api/webhook",
52
+ blueprint_name: str = "aigc_auth_webhook"
53
+ ) -> Blueprint:
54
+ """
55
+ 创建 Flask Webhook Blueprint
56
+
57
+ Args:
58
+ handler: 处理函数,接收 webhook 数据并返回结果
59
+ 函数签名: def handler(data: Dict[str, Any]) -> Dict[str, Any]
60
+ - 如果是同步函数,直接调用
61
+ - 如果是异步函数,会使用 asyncio.run 包装
62
+ secret_env_key: webhook 密钥的环境变量名
63
+ url_prefix: URL 前缀,默认为 "/api/webhook"
64
+ blueprint_name: Blueprint 名称,默认为 "aigc_auth_webhook"
65
+
66
+ Returns:
67
+ Blueprint: Flask Blueprint 实例
68
+
69
+ 使用示例:
70
+ from huace_aigc_auth_client.webhook_flask import create_flask_webhook_blueprint
71
+
72
+ # 方式1:使用适配器的 handle_webhook 方法
73
+ from your_app.adapters import YourAdapter
74
+
75
+ sync_config = create_sync_config(...)
76
+ auth_client = AigcAuthClient(...)
77
+ adapter = YourAdapter(sync_config, auth_client)
78
+
79
+ async def webhook_handler(data: dict) -> dict:
80
+ event = data.get("event")
81
+ event_data = data.get("data", {})
82
+ return await adapter.handle_webhook(event, event_data)
83
+
84
+ webhook_bp = create_flask_webhook_blueprint(
85
+ handler=webhook_handler,
86
+ url_prefix="/api/webhook"
87
+ )
88
+
89
+ # 方式2:自定义处理逻辑
90
+ def custom_handler(data: dict) -> dict:
91
+ event = data.get("event")
92
+ if event == "user.created":
93
+ # 自定义处理逻辑
94
+ user_data = data.get("data", {})
95
+ # ... 处理用户创建事件
96
+ return {"status": "success", "created": True}
97
+ return {"status": "ok"}
98
+
99
+ webhook_bp = create_flask_webhook_blueprint(handler=custom_handler)
100
+
101
+ # 注册到 Flask app
102
+ app.register_blueprint(webhook_bp)
103
+
104
+ # Webhook 端点: POST /api/webhook/auth
105
+ """
106
+ webhook_bp = Blueprint(blueprint_name, __name__, url_prefix=url_prefix)
107
+
108
+ @webhook_bp.route('/auth', methods=['POST'])
109
+ def receive_auth_webhook():
110
+ """
111
+ 接收 aigc-auth 用户变更通知
112
+
113
+ 当 aigc-auth 中创建或更新用户时,会发送 webhook 通知到此端点。
114
+
115
+ 支持的事件:
116
+ - user.created: 用户创建
117
+ - user.updated: 用户更新
118
+ - user.deleted: 用户删除
119
+ - user.login: 用户登录
120
+ - user.init_sync_auth: 初始化同步请求
121
+ """
122
+ # 获取请求体
123
+ body = request.get_data()
124
+
125
+ # 验证签名
126
+ signature = request.headers.get("X-Webhook-Signature", "")
127
+ secret = os.getenv(secret_env_key, "")
128
+
129
+ if not verify_webhook_signature(body, signature, secret):
130
+ logger.warning("Webhook 签名验证失败")
131
+ return jsonify({"code": 401, "message": "Invalid signature"}), 401
132
+
133
+ # 解析请求数据
134
+ try:
135
+ data = request.get_json()
136
+ except Exception as e:
137
+ logger.error(f"解析 webhook 数据失败: {e}")
138
+ return jsonify({"code": 400, "message": "Invalid JSON payload"}), 400
139
+
140
+ # 记录日志
141
+ event = data.get("event", "unknown")
142
+ logger.info(f"收到 webhook 事件: {event}")
143
+
144
+ # 调用用户提供的处理函数
145
+ try:
146
+ import asyncio
147
+ import inspect
148
+
149
+ # 检查 handler 是否为协程函数
150
+ if inspect.iscoroutinefunction(handler):
151
+ result = asyncio.run(handler(data))
152
+ else:
153
+ result = handler(data)
154
+
155
+ return jsonify({"code": 0, "message": "success", "data": result})
156
+
157
+ except Exception as e:
158
+ logger.exception(f"处理 webhook 失败: {e}")
159
+ return jsonify({"code": 500, "message": str(e)}), 500
160
+
161
+ return webhook_bp
162
+
163
+
164
+ def register_flask_webhook_routes(
165
+ app,
166
+ handler: Callable[[Dict[str, Any]], Dict[str, Any]],
167
+ secret_env_key: str = "AIGC_AUTH_WEBHOOK_SECRET",
168
+ url_prefix: str = "/api/webhook",
169
+ blueprint_name: str = "aigc_auth_webhook"
170
+ ):
171
+ """
172
+ 注册 webhook 路由到 Flask 应用
173
+
174
+ 这是一个便捷函数,封装了创建 blueprint 和注册的过程。
175
+
176
+ Args:
177
+ app: Flask 应用实例
178
+ handler: 处理函数,接收 webhook 数据并返回结果
179
+ secret_env_key: webhook 密钥的环境变量名
180
+ url_prefix: URL 前缀,默认为 "/api/webhook"
181
+ blueprint_name: Blueprint 名称
182
+
183
+ 使用示例:
184
+ from huace_aigc_auth_client.webhook_flask import register_flask_webhook_routes
185
+
186
+ app = Flask(__name__)
187
+
188
+ # 创建适配器
189
+ adapter = YourAdapter(sync_config, auth_client)
190
+
191
+ # 定义处理函数
192
+ async def webhook_handler(data: dict) -> dict:
193
+ event = data.get("event")
194
+ event_data = data.get("data", {})
195
+ return await adapter.handle_webhook(event, event_data)
196
+
197
+ # 注册 webhook 路由
198
+ register_flask_webhook_routes(
199
+ app,
200
+ handler=webhook_handler,
201
+ url_prefix="/custom/webhook" # 自定义前缀
202
+ )
203
+
204
+ # Webhook 端点: POST /custom/webhook/auth
205
+ """
206
+ webhook_bp = create_flask_webhook_blueprint(
207
+ handler=handler,
208
+ secret_env_key=secret_env_key,
209
+ url_prefix=url_prefix,
210
+ blueprint_name=blueprint_name
211
+ )
212
+ app.register_blueprint(webhook_bp)
213
+ logger.info(f"Webhook 路由已注册: {url_prefix}/auth")
214
+
215
+
216
+ # 向后兼容的别名
217
+ create_webhook_blueprint = create_flask_webhook_blueprint
218
+ register_webhook_routes = register_flask_webhook_routes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: huace-aigc-auth-client
3
- Version: 1.1.7
3
+ Version: 1.1.9
4
4
  Summary: 华策AIGC Auth Client - 提供 Token 验证、用户信息获取、权限检查、旧系统接入等功能
5
5
  Author-email: Huace <support@huace.com>
6
6
  License: MIT
@@ -44,8 +44,7 @@ pip install huace-aigc-auth-client
44
44
  # 必填:应用 ID 和密钥(在鉴权中心创建应用后获取)
45
45
  AIGC_AUTH_APP_ID=your_app_id
46
46
  AIGC_AUTH_APP_SECRET=your_app_secret
47
-
48
- # 可选:鉴权服务地址(默认为生产环境)- 测试环境鉴权地址:https://auth-test.aigc.huacemedia.com/aigc-auth/api/v1
47
+ # 鉴权服务地址(默认为生产环境)- 测试环境鉴权地址:http://auth.aigc-test.huacemedia.com/aigc-auth/api/v1
49
48
  AIGC_AUTH_BASE_URL=https://aigc-auth.huacemedia.com/aigc-auth/api/v1
50
49
  ```
51
50
 
@@ -420,8 +419,8 @@ from huace_aigc_auth_client import LegacySystemAdapter, LegacyUserData
420
419
  class MyLegacyAdapter(LegacySystemAdapter):
421
420
  """实现与旧系统用户表的交互"""
422
421
 
423
- def __init__(self, db, sync_config):
424
- super().__init__(sync_config)
422
+ def __init__(self, db, sync_config, auth_client=None):
423
+ super().__init__(sync_config, auth_client)
425
424
  self.db = db
426
425
 
427
426
  def get_user_by_unique_field(self, username: str):
@@ -435,8 +434,23 @@ class MyLegacyAdapter(LegacySystemAdapter):
435
434
  "email": user.email,
436
435
  # ... 其他字段
437
436
  })
437
+
438
+ async def get_user_by_username_async(self, username: str) -> Optional[LegacyUserData]:
439
+ """异步通过用户名获取旧系统用户"""
440
+ user = await self.db.execute(
441
+ select(User).where(User.username == username)
442
+ )
443
+ user = user.scalars().first()
444
+ if not user:
445
+ return None
446
+ return LegacyUserData({
447
+ "id": user.id,
448
+ "username": user.username,
449
+ "email": user.email,
450
+ # ... 其他字段
451
+ })
438
452
 
439
- def create_user(self, user_data: dict):
453
+ async def _create_user_async(self, user_data: Dict[str, Any]) -> Optional[Any]:
440
454
  """在旧系统创建用户"""
441
455
  user = User(
442
456
  username=user_data["username"],
@@ -445,20 +459,39 @@ class MyLegacyAdapter(LegacySystemAdapter):
445
459
  # ... 其他字段
446
460
  )
447
461
  self.db.add(user)
448
- self.db.commit()
462
+ await self.db.commit()
449
463
  return user.id
450
464
 
451
- def update_user(self, username: str, user_data: dict):
452
- """更新旧系统用户"""
453
- user = self.db.query(User).filter(User.username == username).first()
465
+ async def _update_user_async(self, username: str, user_data: Dict[str, Any]) -> bool:
466
+ """异步更新旧系统用户"""
467
+ user = await self.db.execute(
468
+ select(User).where(User.username == username)
469
+ )
470
+ user = user.scalars().first()
454
471
  if user:
455
472
  for key, value in user_data.items():
456
473
  setattr(user, key, value)
457
- self.db.commit()
474
+ await self.db.commit()
475
+ return True
476
+ return False
477
+
478
+ async def _delete_user_async(self, username: str) -> bool:
479
+ """异步删除旧系统用户"""
480
+ user = await self.db.execute(
481
+ select(User).where(User.username == username)
482
+ )
483
+ user = user.scalars().first()
484
+ if user:
485
+ await self.db.delete(user)
486
+ # 或者软删除:user.is_active = False
487
+ await self.db.commit()
488
+ return True
489
+ return False
458
490
 
459
- def get_all_users(self):
460
- """获取所有用户(用于初始化同步)"""
461
- users = self.db.query(User).all()
491
+ async def get_all_users_async(self) -> List[LegacyUserData]:
492
+ """异步获取所有用户(用于初始化同步)"""
493
+ result = await self.db.execute(select(User))
494
+ users = result.scalars().all()
462
495
  return [LegacyUserData({"username": u.username, ...}) for u in users]
463
496
  ```
464
497
 
@@ -468,7 +501,7 @@ class MyLegacyAdapter(LegacySystemAdapter):
468
501
  from huace_aigc_auth_client import AigcAuthClient, UserSyncService
469
502
 
470
503
  client = AigcAuthClient()
471
- adapter = MyLegacyAdapter(db, sync_config)
504
+ adapter = MyLegacyAdapter(db, sync_config, client)
472
505
  sync_service = UserSyncService(client, adapter)
473
506
 
474
507
  # 在获取用户信息后调用同步
@@ -479,7 +512,7 @@ async def auth_middleware(request, call_next):
479
512
  # 登录成功后,同步用户到旧系统
480
513
  if hasattr(request.state, "user_info"):
481
514
  user_info = request.state.user_info
482
- sync_service.sync_on_login(user_info)
515
+ await sync_service.sync_on_login_async(user_info)
483
516
 
484
517
  return response
485
518
  ```
@@ -493,47 +526,35 @@ from fastapi import APIRouter
493
526
  from huace_aigc_auth_client import register_webhook_router
494
527
 
495
528
  api_router = APIRouter()
529
+ client = AigcAuthClient()
530
+ adapter = MyLegacyAdapter(db, sync_config, client)
496
531
 
497
532
  # 定义 webhook 处理函数
498
- async def handle_webhook(data: dict) -> dict:
533
+ async def handle_auth_webhook(request_data: Dict[str, Any]) -> Dict[str, Any]:
499
534
  """
500
- 处理来自 aigc-auth webhook 通知
535
+ 独立的 webhook 处理函数(用于 SDK 集成)
501
536
 
502
- Args:
503
- data: webhook 数据,包含 event 和 data 字段
504
-
505
- Returns:
506
- dict: 处理结果
537
+ 这个函数不需要 db 参数,会在内部创建数据库会话。
538
+ 适用于通过 SDK register_webhook_router 注册。
507
539
  """
508
- from your_app.db import get_db_session
540
+ from app.db.session import get_db
509
541
 
510
- event = data.get("event")
511
- user_data = data.get("data", {})
512
-
513
- # 创建数据库会话
514
- async with get_db_session() as db:
515
- adapter = MyLegacyAdapter(db, sync_config)
516
-
517
- if event == "user.created":
518
- # 处理用户创建事件
519
- if not adapter.get_user_by_unique_field(user_data["username"]):
520
- legacy_data = adapter.transform_auth_to_legacy(user_data)
521
- legacy_data["password"] = sync_config.unified_password
522
- adapter.create_user(legacy_data)
523
- await db.commit()
524
-
525
- elif event == "user.updated":
526
- # 处理用户更新事件
527
- legacy_data = adapter.transform_auth_to_legacy(user_data)
528
- adapter.update_user(user_data["username"], legacy_data)
529
- await db.commit()
530
-
531
- return {"status": "ok", "event": event}
542
+ async for db in get_db():
543
+ try:
544
+ event = request_data.get("event")
545
+ data = request_data.get("data", {})
546
+ logger.info(f"Received webhook event: {event} with data: {data}")
547
+
548
+ # 调用适配器的 handle_webhook 方法
549
+ return await adapter.handle_webhook(event, data)
550
+ except Exception as e:
551
+ logger.exception(f"Failed to handle webhook: {e}")
552
+ raise
532
553
 
533
554
  # 注册 webhook 路由(自动处理签名验证)
534
555
  register_webhook_router(
535
556
  api_router,
536
- handler=handle_webhook,
557
+ handler=handle_auth_webhook,
537
558
  prefix="/webhook", # 可选,默认 "/webhook"
538
559
  secret_env_key="aigc-auth-webhook-secret", # 可选,在 auth 后台配置
539
560
  tags=["Webhook"] # 可选,默认 ["Webhook"]
@@ -542,55 +563,6 @@ register_webhook_router(
542
563
  # webhook 端点将自动创建在: /webhook/auth
543
564
  ```
544
565
 
545
- **传统方式:手动实现 Webhook 端点**
546
-
547
- ```python
548
- import hmac
549
- import hashlib
550
- import os
551
-
552
- @app.post("/api/v1/webhook/auth")
553
- async def receive_auth_webhook(request: Request, db: Session = Depends(get_db)):
554
- """接收 aigc-auth 的用户变更通知"""
555
- # 获取原始请求体
556
- body = await request.body()
557
-
558
- # 验证签名
559
- signature = request.headers.get("X-Webhook-Signature", "")
560
- secret = os.getenv("AIGC_AUTH_WEBHOOK_SECRET", "")
561
-
562
- if secret:
563
- expected = hmac.new(
564
- secret.encode('utf-8'),
565
- body,
566
- hashlib.sha256
567
- ).hexdigest()
568
-
569
- if not hmac.compare_digest(expected, signature):
570
- raise HTTPException(status_code=401, detail="Invalid signature")
571
-
572
- # 解析数据
573
- data = await request.json()
574
- event = data.get("event")
575
- user_data = data.get("data", {})
576
-
577
- if event == "user.created":
578
- adapter = MyLegacyAdapter(db, sync_config)
579
- if not adapter.get_user_by_unique_field(user_data["username"]):
580
- legacy_data = adapter.transform_auth_to_legacy(user_data)
581
- legacy_data["password"] = sync_config.unified_password
582
- adapter.create_user(legacy_data)
583
- db.commit()
584
-
585
- elif event == "user.updated":
586
- adapter = MyLegacyAdapter(db, sync_config)
587
- legacy_data = adapter.transform_auth_to_legacy(user_data)
588
- adapter.update_user(user_data["username"], legacy_data)
589
- db.commit()
590
-
591
- return {"success": True}
592
- ```
593
-
594
566
  **Webhook 签名验证说明**
595
567
 
596
568
  SDK 的 `register_webhook_router` 会自动处理签名验证:
@@ -604,7 +576,7 @@ SDK 的 `register_webhook_router` 会自动处理签名验证:
604
576
  ```python
605
577
  # 一次性脚本:将旧系统用户同步到 aigc-auth
606
578
  async def init_sync():
607
- adapter = MyLegacyAdapter(db, sync_config)
579
+ adapter = MyLegacyAdapter(db, sync_config, client)
608
580
  users = adapter.get_all_users()
609
581
 
610
582
  for user in users:
@@ -7,6 +7,7 @@ huace_aigc_auth_client/__init__.py
7
7
  huace_aigc_auth_client/legacy_adapter.py
8
8
  huace_aigc_auth_client/sdk.py
9
9
  huace_aigc_auth_client/webhook.py
10
+ huace_aigc_auth_client/webhook_flask.py
10
11
  huace_aigc_auth_client.egg-info/PKG-INFO
11
12
  huace_aigc_auth_client.egg-info/SOURCES.txt
12
13
  huace_aigc_auth_client.egg-info/dependency_links.txt
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "huace-aigc-auth-client"
7
- version = "1.1.7"
7
+ version = "1.1.9"
8
8
  description = "华策AIGC Auth Client - 提供 Token 验证、用户信息获取、权限检查、旧系统接入等功能"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.7"