Files
X25020501_card_book/core/routers/auth/services.py

95 lines
3.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- 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('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