Files
geMoldInsight/src/services/auth_service.py
T

146 lines
4.3 KiB
Python
Raw Normal View History

2026-03-04 00:47:41 +08:00
from datetime import datetime, timedelta
from typing import Optional
from jose import JWTError, jwt
from passlib.context import CryptContext
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from sqlalchemy.orm import selectinload
from config.settings import settings
from database.database import get_db_session
from models.database import User
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/auth/login", auto_error=False)
def verify_password(plain_password: str, hashed_password: str) -> bool:
return pwd_context.verify(plain_password, hashed_password)
def get_password_hash(password: str) -> str:
return pwd_context.hash(password)
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -> str:
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
return encoded_jwt
async def get_current_user(
token: Optional[str] = Depends(oauth2_scheme),
db_session: AsyncSession = Depends(get_db_session)
) -> Optional[User]:
if not token:
return None
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="无法验证凭据",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
except JWTError:
raise credentials_exception
result = await db_session.execute(
select(User).where(User.username == username)
)
user = result.scalar_one_or_none()
if user is None:
raise credentials_exception
if not user.is_active:
raise HTTPException(status_code=400, detail="用户已被禁用")
return user
async def get_current_active_user(
current_user: Optional[User] = Depends(get_current_user)
) -> User:
if not current_user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="请先登录",
headers={"WWW-Authenticate": "Bearer"},
)
return current_user
async def get_current_admin_user(
current_user: User = Depends(get_current_active_user)
) -> User:
if not current_user.is_superuser:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="需要管理员权限"
)
return current_user
async def authenticate_user(db_session: AsyncSession, username: str, password: str) -> Optional[User]:
result = await db_session.execute(
select(User).where(User.username == username)
)
user = result.scalar_one_or_none()
if not user:
return None
if not verify_password(password, user.hashed_password):
return None
user.last_login = datetime.utcnow()
await db_session.commit()
return user
async def create_user(
db_session: AsyncSession,
username: str,
email: str,
password: str,
full_name: Optional[str] = None,
is_superuser: bool = False
) -> User:
hashed_password = get_password_hash(password)
user = User(
username=username,
email=email,
hashed_password=hashed_password,
full_name=full_name,
is_superuser=is_superuser,
is_active=True
)
db_session.add(user)
await db_session.commit()
await db_session.refresh(user)
return user
async def get_user_by_username(db_session: AsyncSession, username: str) -> Optional[User]:
result = await db_session.execute(
select(User).where(User.username == username)
)
return result.scalar_one_or_none()
async def get_user_by_email(db_session: AsyncSession, email: str) -> Optional[User]:
result = await db_session.execute(
select(User).where(User.email == email)
)
return result.scalar_one_or_none()