""" 依赖注入 """ from fastapi import Depends, HTTPException, status, Request from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from sqlalchemy.orm import Session from typing import Optional from app.database import get_db from app.core.security import verify_token from app.models.admin import Admin from app.models.user import User from app.models.session import Session from app.services.session_service import SessionService security = HTTPBearer() def get_current_admin( credentials: HTTPAuthorizationCredentials = Depends(security), db: Session = Depends(get_db) ) -> Admin: """获取当前管理员""" token = credentials.credentials payload = verify_token(token) if not payload: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="无效的令牌" ) username = payload.get("sub") admin = db.query(Admin).filter(Admin.username == username).first() if not admin or admin.status != 1: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="管理员不存在或已被禁用" ) return admin def get_current_user_session( request: Request, db: Session = Depends(get_db), allow_expired_extend: bool = False ) -> tuple[User, Session]: """ 获取当前用户会话 从请求头或查询参数中获取session_id,验证并返回用户和会话信息 Args: allow_expired_extend: 是否允许延长已过期的活跃Session(用于心跳场景) Returns: tuple: (User对象, Session对象) """ # 从请求头获取session_id session_id = request.headers.get("X-Session-ID") # 如果请求头中没有,从查询参数获取 if not session_id: session_id = request.query_params.get("session_id") # 如果都没有,从请求体中获取(针对加密请求) if not session_id: # 对于加密请求,session_id会在解密后的数据中 # 这里先设置为空,后续在接口中处理 pass if not session_id: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="缺少会话ID" ) # 验证会话(根据是否为心跳请求决定是否允许延长过期Session) is_valid, session, error_msg = SessionService.validate_session(db, session_id, allow_expired_extend=allow_expired_extend) if not is_valid: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail=f"会话验证失败: {error_msg}" ) # 获取用户信息 user = db.query(User).filter(User.id == session.user_id).first() if not user: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="用户不存在" ) # 验证header中的user_id和user_name是否与session匹配(组合校验) header_user_id = request.headers.get("x-user-id") or request.headers.get("X-User-Id") header_user_name = request.headers.get("x-user-name") or request.headers.get("X-User-Name") if header_user_id and header_user_name: try: header_user_id = int(header_user_id) if header_user_id != user.id or header_user_name != user.username: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="用户信息校验失败" ) except ValueError: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="用户信息格式错误" ) else: # 如果没有提供header用户信息,认为是认证失败 raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="缺少用户信息" ) return user, session def get_current_user_session_for_heartbeat( request: Request, db: Session = Depends(get_db) ) -> tuple[User, Session]: """ 获取当前用户会话(心跳专用) 允许延长已过期的活跃Session,保持用户在线体验 """ return get_current_user_session(request, db, allow_expired_extend=True) def get_optional_user_session( request: Request, db: Session = Depends(get_db) ) -> Optional[tuple[User, Session]]: """ 获取可选的用户会话(用于不需要强制登录的接口) Returns: Optional[tuple[User, Session]]: 用户和会话信息,如果没有则返回None """ try: return get_current_user_session(request, db) except HTTPException: return None