# -*- coding = utf-8 -*- # @Time : 2025/2/17 下午8:14 # @File : services.py # @Software : PyCharm # @Author : xingc # @Desc : from datetime import datetime, timedelta from typing import Annotated, Optional import jwt import ujson from fastapi import HTTPException, status, Depends, Request, WebSocket from fastapi.security import OAuth2PasswordBearer import redis.asyncio as aioredis from sqlalchemy.ext.asyncio import AsyncSession from config import settings from core.db.crud import CRUD from core.db.engine import get_async_session, get_cache from core.db.models import User from core.utils.helper import generate_keyname from . import schema oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") def create_access_token(data: dict): to_encode = data.copy() expire = datetime.utcnow() + timedelta( minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES ) to_encode.update({"exp": expire}) encoded_jwt = jwt.encode(to_encode, settings.ACCESS_SECRET_KEY, algorithm=settings.ALGORITHM) return {'token_type': 'bearer', 'access_token': encoded_jwt} def verify_access_token(token: str): try: # payload = jwt.decode(token, settings.ACCESS_SECRET_KEY, algorithms=[settings.ALGORITHM]) payload = jwt.decode(token, settings.ACCESS_SECRET_KEY, algorithms=[settings.ALGORITHM]) return payload except jwt.ExpiredSignatureError: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Token has expired") except jwt.InvalidTokenError: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token") async def get_current_user( token: str | None, db_session: AsyncSession, redis_conn: aioredis ) -> schema.LoginUser: """验证登录状态、获取的用户基础信息""" result = verify_access_token(token) user_id = result.get("sub") if user_id is None: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token") keyname = generate_keyname(f'login_cache', use_today=True) user_str = await redis_conn.hget(keyname, user_id) if user_str is None: user_model = await CRUD(db_session, User).get(filters={'user_id': user_id}) user = schema.LoginUser(**user_model.dict()) await redis_conn.hset(keyname, user_id, user.json()) else: user = schema.LoginUser(**ujson.decode(user_str)) return user async def get_current_user_form_http( token: Annotated[str, Depends(oauth2_scheme)], db_session: Annotated[AsyncSession, Depends(get_async_session)], redis_conn: aioredis = Depends(get_cache), ) -> schema.LoginUser: """在http headers中提取token,并验证再获取当前用户""" user = await get_current_user(token, db_session, redis_conn) return user async def get_current_user_form_ws( websocket: WebSocket, db_session: Annotated[AsyncSession, Depends(get_async_session)] = None, redis_conn: aioredis = Depends(get_cache), ) -> schema.LoginUser: """在ws headers中提取token,并验证再获取当前用户""" auth_header = websocket.headers.get("authorization") or websocket.headers.get("Authorization") if auth_header and auth_header.lower().startswith("bearer "): token = auth_header.removeprefix("Bearer ").strip() else: token = '' user = await get_current_user(token, db_session, redis_conn) return user