auth.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. """
  2. 认证中间件(ASGI)- 管理端专用
  3. - 验证 Authorization: Bearer <token>
  4. - 校验管理员存在性与状态
  5. - 在 scope 中注入 verified_admin_id 供下游中间件使用
  6. """
  7. from app.core.security import verify_token
  8. from app.database import SessionLocal
  9. from app.models.admin import Admin
  10. from fastapi.responses import JSONResponse
  11. from app.core.logger import logger, get_logger_by_path
  12. EXEMPT_PATHS = [
  13. "/",
  14. "/admin/login",
  15. "/health",
  16. "/docs",
  17. "/redoc",
  18. "/openapi.json",
  19. ]
  20. class AuthValidationASGIMiddleware:
  21. def __init__(self, app):
  22. self.app = app
  23. async def __call__(self, scope, receive, send):
  24. # 仅处理 HTTP 请求
  25. if scope.get("type") != "http":
  26. await self.app(scope, receive, send)
  27. return
  28. path = scope.get("path", "")
  29. method = scope.get("method", "").upper()
  30. # 根据路径选择对应的logger
  31. auth_logger = get_logger_by_path(path)
  32. # 跳过不需要验证的路径或 CORS 预检
  33. if method == "OPTIONS" or any(path.startswith(p) for p in EXEMPT_PATHS):
  34. await self.app(scope, receive, send)
  35. return
  36. headers = {k.decode("utf-8"): v.decode("utf-8") for k, v in scope.get("headers", [])}
  37. auth_header = headers.get("authorization", "") or headers.get("Authorization", "")
  38. # 获取header中的用户信息用于组合校验
  39. header_user_id = headers.get("x-user-id") or headers.get("X-User-Id")
  40. header_user_name = headers.get("x-user-name") or headers.get("X-User-Name")
  41. # 临时调试日志:记录客户端传来的鉴别头,帮助定位客户端是否传递了 X-User-Id/X-User-Name 或 Authorization
  42. try:
  43. auth_logger.debug(f"[AuthMiddleware][DEBUG HEADERS] path={path} method={method} X-User-Id={header_user_id} X-User-Name={header_user_name} AuthorizationPresent={bool(auth_header)}")
  44. except Exception:
  45. auth_logger.debug("[AuthMiddleware][DEBUG HEADERS] failed to read headers")
  46. if not auth_header or not auth_header.startswith("Bearer "):
  47. resp = JSONResponse(status_code=401, content={"code": 401, "msg": "认证信息缺失或无效"})
  48. await resp(scope, receive, send)
  49. return
  50. token = auth_header[7:]
  51. payload = verify_token(token)
  52. if not payload:
  53. resp = JSONResponse(status_code=401, content={"code": 401, "msg": "认证信息无效或已过期"})
  54. await resp(scope, receive, send)
  55. return
  56. username = payload.get("sub")
  57. if not username:
  58. resp = JSONResponse(status_code=401, content={"code": 401, "msg": "认证信息无效"})
  59. await resp(scope, receive, send)
  60. return
  61. auth_logger.debug(f"[AuthMiddleware][DEBUG] Starting authentication for username: {username}, path: {path}")
  62. # 查询管理员
  63. db = SessionLocal()
  64. try:
  65. admin = db.query(Admin).filter(Admin.username == username).first()
  66. if admin:
  67. auth_logger.debug(f"[AuthMiddleware][DEBUG] Found admin: {admin.username} (ID: {admin.id})")
  68. # 验证header中的用户信息是否匹配(组合校验)
  69. if header_user_id and header_user_name:
  70. try:
  71. header_user_id_int = int(header_user_id)
  72. if header_user_id_int != admin.id or header_user_name != admin.username:
  73. resp = JSONResponse(status_code=401, content={"code": 401, "msg": "用户信息校验失败"})
  74. await resp(scope, receive, send)
  75. return
  76. except ValueError:
  77. resp = JSONResponse(status_code=401, content={"code": 401, "msg": "用户信息格式错误"})
  78. await resp(scope, receive, send)
  79. return
  80. else:
  81. resp = JSONResponse(status_code=401, content={"code": 401, "msg": "缺少用户信息"})
  82. await resp(scope, receive, send)
  83. return
  84. if admin.status != 1:
  85. resp = JSONResponse(status_code=403, content={"code": 403, "msg": "管理员账号已被禁用"})
  86. await resp(scope, receive, send)
  87. return
  88. scope["verified_admin_id"] = admin.id
  89. scope["verified_username"] = admin.username
  90. scope["verified_is_admin"] = True
  91. auth_logger.debug(f"[AuthMiddleware][DEBUG] Set verified_admin_id: {admin.id} for admin: {admin.username}")
  92. await self.app(scope, receive, send)
  93. return
  94. # 未找到对应管理员
  95. auth_logger.debug(f"[AuthMiddleware][DEBUG] No admin found for username: {username}")
  96. resp = JSONResponse(status_code=401, content={"code": 401, "msg": "认证失败:管理员不存在"})
  97. await resp(scope, receive, send)
  98. return
  99. except Exception as e:
  100. auth_logger.exception(f"[AuthMiddleware][ERROR] Exception during authentication: {e}")
  101. resp = JSONResponse(status_code=500, content={"code": 500, "msg": "认证服务异常"})
  102. await resp(scope, receive, send)
  103. return
  104. finally:
  105. db.close()