157 lines
6.0 KiB
Python
157 lines
6.0 KiB
Python
from typing import Optional
|
||
|
||
import requests
|
||
from fastapi import Header, HTTPException, status, Cookie
|
||
from pydantic import BaseModel
|
||
|
||
from .config import settings
|
||
|
||
|
||
class AuthedUser(BaseModel):
|
||
id: int
|
||
reg_step: int = 1
|
||
gender: int = 0 # 0/1/2
|
||
nickname: str = ""
|
||
token: str = ""
|
||
|
||
|
||
def _fetch_user_from_php(token: str) -> Optional[dict]:
|
||
"""通过 PHP/FastAdmin 接口获取用户信息。"""
|
||
import logging
|
||
logger = logging.getLogger(__name__)
|
||
|
||
# 从配置读取 PHP 服务地址,如果配置不可用则使用本地地址
|
||
try:
|
||
from lover.config import settings
|
||
user_info_api = settings.USER_INFO_API
|
||
except:
|
||
# 默认使用本地地址
|
||
user_info_api = "http://127.0.0.1:30100/api/user_basic/get_user_basic"
|
||
|
||
logger.info(f"用户中心调试 - 调用接口: {user_info_api}")
|
||
logger.info(f"用户中心调试 - token: {token}")
|
||
|
||
try:
|
||
resp = requests.get(
|
||
user_info_api,
|
||
headers={"token": token},
|
||
timeout=3, # 减少超时时间到3秒
|
||
)
|
||
logger.info(f"用户中心调试 - 响应状态码: {resp.status_code}")
|
||
logger.info(f"用户中心调试 - 响应内容: {resp.text[:200]}...")
|
||
except requests.exceptions.Timeout:
|
||
logger.error(f"用户中心调试 - 请求超时(3秒)")
|
||
raise HTTPException(
|
||
status_code=status.HTTP_502_BAD_GATEWAY,
|
||
detail="用户中心接口超时",
|
||
)
|
||
except requests.exceptions.ConnectionError as exc:
|
||
logger.error(f"用户中心调试 - 连接失败: {exc}")
|
||
raise HTTPException(
|
||
status_code=status.HTTP_502_BAD_GATEWAY,
|
||
detail="无法连接到用户中心",
|
||
)
|
||
except Exception as exc: # 其他异常
|
||
logger.error(f"用户中心调试 - 请求异常: {exc}")
|
||
raise HTTPException(
|
||
status_code=status.HTTP_502_BAD_GATEWAY,
|
||
detail="用户中心接口不可用",
|
||
) from exc
|
||
|
||
if resp.status_code != 200:
|
||
logger.error(f"用户中心调试 - 状态码非200: {resp.status_code}")
|
||
raise HTTPException(
|
||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||
detail="用户中心返回非 200",
|
||
)
|
||
data = resp.json()
|
||
logger.info(f"用户中心调试 - 解析JSON成功: {data}")
|
||
# 兼容常见 FastAdmin 响应结构
|
||
if isinstance(data, dict):
|
||
payload = data.get("data") or data.get("user") or data
|
||
else:
|
||
payload = None
|
||
if not payload:
|
||
logger.error(f"用户中心调试 - 未找到用户数据: {data}")
|
||
raise HTTPException(
|
||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||
detail="用户中心未返回用户信息",
|
||
)
|
||
return payload
|
||
|
||
|
||
def get_current_user(
|
||
authorization: Optional[str] = Header(default=None),
|
||
x_token: Optional[str] = Header(default=None, alias="X-Token"),
|
||
token_header: Optional[str] = Header(default=None, alias="token"),
|
||
token_cookie: Optional[str] = Cookie(default=None, alias="token"),
|
||
x_user_id: Optional[int] = Header(default=None, alias="X-User-Id"),
|
||
):
|
||
"""
|
||
鉴权顺序:
|
||
1) Authorization: Bearer <token>
|
||
2) X-Token: <token>
|
||
3) token: <token> (header)
|
||
4) token: <token> (cookie)
|
||
5) X-User-Id(仅调试用,不经过 PHP 鉴权)
|
||
生产流程:直接调用 USER_INFO_API 拉取用户信息,不再依赖本地 nf_user 缓存。
|
||
"""
|
||
import logging
|
||
logger = logging.getLogger(__name__)
|
||
|
||
logger.info(f"认证调试 - APP_ENV: {settings.APP_ENV}, DEBUG: {settings.DEBUG}")
|
||
logger.info(f"认证调试 - authorization: {authorization}, x_token: {x_token}, token_header: {token_header}, token_cookie: {token_cookie}, x_user_id: {x_user_id}")
|
||
|
||
token = None
|
||
if authorization and authorization.lower().startswith("bearer "):
|
||
token = authorization.split(" ", 1)[1].strip()
|
||
if not token and x_token:
|
||
token = x_token
|
||
if not token and token_header:
|
||
token = token_header
|
||
if not token and token_cookie:
|
||
token = token_cookie
|
||
|
||
logger.info(f"认证调试 - 提取的 token: {token}")
|
||
|
||
if token:
|
||
try:
|
||
payload = _fetch_user_from_php(token)
|
||
user_id = payload.get("id") or payload.get("user_id")
|
||
reg_step = payload.get("reg_step") or payload.get("stage") or 1
|
||
gender = payload.get("gender") or 0
|
||
nickname = payload.get("nickname") or payload.get("username") or ""
|
||
|
||
# 开发环境:自动提升 reg_step 到 2,方便测试
|
||
if settings.APP_ENV == "development" and settings.DEBUG and reg_step < 2:
|
||
logger.warning(f"开发环境:用户 reg_step={reg_step},自动提升到 2")
|
||
reg_step = 2
|
||
|
||
if not user_id:
|
||
raise HTTPException(
|
||
status_code=status.HTTP_401_UNAUTHORIZED, detail="用户中心缺少用户ID"
|
||
)
|
||
return AuthedUser(
|
||
id=user_id,
|
||
reg_step=reg_step,
|
||
gender=gender,
|
||
nickname=nickname,
|
||
token=token,
|
||
)
|
||
except HTTPException as e:
|
||
# 如果是开发环境,token 验证失败时也返回测试用户
|
||
if settings.APP_ENV == "development" and settings.DEBUG:
|
||
logger.warning(f"开发环境:token 验证失败({e.detail}),使用测试用户")
|
||
return AuthedUser(id=70, reg_step=2, gender=0, nickname="test-user", token="")
|
||
raise
|
||
|
||
# 调试兜底:仅凭 X-User-Id 不校验 PHP,方便联调
|
||
if x_user_id is not None:
|
||
return AuthedUser(id=x_user_id, reg_step=2, gender=0, nickname="debug-user", token="")
|
||
|
||
# 开发环境兜底:如果没有任何认证信息,返回默认测试用户
|
||
if settings.APP_ENV == "development" and settings.DEBUG:
|
||
return AuthedUser(id=70, reg_step=2, gender=0, nickname="test-user", token="")
|
||
|
||
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="用户不存在或未授权")
|