Files
sanguo_vnpy_v2/sanguo_web/api/deps.py
T
claude_dev 918bbed0fc fix: 修复登录500错误和移除明文密码提示
- 修复 deps.py 中 get_vn_service 的引用错误 (vn_service.vn_service -> vn_service)
- 移除登录页面上的明文密码提示
- 改进前端错误处理,避免数据加载失败导致登录显示错误
2026-07-02 12:23:55 +08:00

177 lines
4.8 KiB
Python

"""
依赖注入模块
提供认证、数据库等依赖注入函数
"""
from typing import Generator, Optional
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from jose import JWTError, jwt
from datetime import datetime, timedelta
import logging
logger = logging.getLogger(__name__)
# JWT 配置
SECRET_KEY = "sanguo_secret_key_change_in_production" # 生产环境应从配置读取
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
security = HTTPBearer()
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -> str:
"""创建 JWT Token"""
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
def verify_token(token: str) -> dict:
"""验证 JWT Token"""
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
return payload
except JWTError as e:
logger.warning(f"Token verification failed: {e}")
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid authentication credentials",
headers={"WWW-Authenticate": "Bearer"},
)
async def get_current_user(
credentials: HTTPAuthorizationCredentials = Depends(security)
) -> dict:
"""
获取当前用户依赖
从 Authorization header 中解析 JWT Token
"""
token = credentials.credentials
try:
payload = verify_token(token)
username: str = payload.get("sub")
if username is None:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid authentication credentials",
headers={"WWW-Authenticate": "Bearer"},
)
# 返回用户信息(实际应从数据库获取)
return {
"username": username,
"is_active": payload.get("is_active", True),
"exp": payload.get("exp")
}
except HTTPException:
raise
except Exception as e:
logger.error(f"Error getting current user: {e}")
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
async def get_optional_user(
credentials: Optional[HTTPAuthorizationCredentials] = Depends(HTTPBearer(auto_error=False))
) -> Optional[dict]:
"""
可选的用户认证依赖
允许未登录用户访问,但如果提供了 Token 则会验证
"""
if credentials is None:
return None
try:
return await get_current_user(credentials)
except HTTPException:
return None
# ============================================
# VeighNa 服务依赖
# ============================================
async def get_vn_service():
"""
获取 VeighNa 服务实例
如果服务未初始化,抛出异常
使用延迟导入避免循环导入问题
"""
from . import vn_service
if vn_service is None:
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail="VeighNa service is not initialized"
)
return vn_service
# ============================================
# 简单认证(开发环境)
# ============================================
# 硬编码的用户数据库(生产环境应使用真实数据库)
FAKE_USERS_DB = {
"admin": {
"username": "admin",
"full_name": "Administrator",
"email": "admin@sanguo.com",
"hashed_password": "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36Wh0/mqGKnKM0lQ5lEqxKe", # "secret"
"is_active": True,
}
}
def authenticate_user(username: str, password: str) -> Optional[dict]:
"""
验证用户凭证
开发环境使用硬编码用户,生产环境应使用数据库
"""
# 开发环境简单验证
if username == "admin" and password == "admin123":
return {
"username": "admin",
"is_active": True
}
# 生产环境应使用数据库和 passlib
# user = FAKE_USERS_DB.get(username)
# if not user:
# return None
# if not user["is_active"]:
# return None
# from passlib.context import CryptContext
# pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# if not pwd_context.verify(password, user["hashed_password"]):
# return None
# return user
return None
__all__ = [
"create_access_token",
"verify_token",
"get_current_user",
"get_optional_user",
"get_vn_service",
"authenticate_user",
]