You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
130 lines
3.6 KiB
130 lines
3.6 KiB
"""认证 API 路由 - 登录/登出/令牌验证/用户信息"""
|
|
|
|
import logging
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, status
|
|
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app import auth_service
|
|
from app.database import get_db
|
|
from app.schemas import LoginRequest, LoginResponse
|
|
|
|
logger = logging.getLogger(__name__)
|
|
router = APIRouter(prefix="/auth", tags=["用户认证"])
|
|
security = HTTPBearer()
|
|
|
|
|
|
async def get_current_user(
|
|
credentials: HTTPAuthorizationCredentials = Depends(security),
|
|
db: AsyncSession = Depends(get_db),
|
|
) -> object:
|
|
"""依赖注入 - 获取当前已登录用户。"""
|
|
user = await auth_service.verify_token(credentials.credentials, db)
|
|
if not user:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail="请先登录",
|
|
)
|
|
return user
|
|
|
|
|
|
async def require_admin(
|
|
current_user=Depends(get_current_user),
|
|
) -> object:
|
|
"""依赖注入 - 验证管理员权限。"""
|
|
if current_user.role != "admin":
|
|
raise HTTPException(
|
|
status_code=status.HTTP_403_FORBIDDEN,
|
|
detail="需要管理员权限",
|
|
)
|
|
return current_user
|
|
|
|
|
|
@router.post("/login", response_model=LoginResponse)
|
|
async def login(
|
|
request: LoginRequest,
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""用户登录。"""
|
|
user = await auth_service.authenticate_user(db, request.username, request.password)
|
|
|
|
if not user:
|
|
return LoginResponse(success=False, message="用户名或密码错误")
|
|
|
|
token = await auth_service.create_token(user.id, db)
|
|
await auth_service.update_last_login(user, db)
|
|
|
|
return LoginResponse(
|
|
success=True,
|
|
token=token,
|
|
user={
|
|
"id": user.id,
|
|
"username": user.username,
|
|
"role": user.role,
|
|
"email": user.email,
|
|
},
|
|
message="登录成功",
|
|
)
|
|
|
|
|
|
@router.post("/logout")
|
|
async def logout(
|
|
credentials: HTTPAuthorizationCredentials = Depends(security),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""用户登出。"""
|
|
await auth_service.invalidate_session(credentials.credentials, db)
|
|
return {"success": True, "message": "已登出"}
|
|
|
|
|
|
@router.get("/verify")
|
|
async def verify_token(
|
|
credentials: HTTPAuthorizationCredentials = Depends(security),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""验证用户令牌。"""
|
|
user = await auth_service.verify_token(credentials.credentials, db)
|
|
|
|
if not user:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail="无效的会话令牌",
|
|
)
|
|
|
|
return {
|
|
"success": True,
|
|
"user": {
|
|
"id": user.id,
|
|
"username": user.username,
|
|
"role": user.role,
|
|
"email": user.email,
|
|
"last_login": user.last_login.isoformat() if user.last_login else None,
|
|
},
|
|
}
|
|
|
|
|
|
@router.get("/me")
|
|
async def get_me(
|
|
credentials: HTTPAuthorizationCredentials = Depends(security),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""获取当前用户信息。"""
|
|
user = await auth_service.verify_token(credentials.credentials, db)
|
|
|
|
if not user:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail="无效的会话令牌",
|
|
)
|
|
|
|
return {
|
|
"id": user.id,
|
|
"username": user.username,
|
|
"role": user.role,
|
|
"email": user.email,
|
|
"is_active": user.is_active,
|
|
"created_at": user.created_at.isoformat(),
|
|
"last_login": user.last_login.isoformat() if user.last_login else None,
|
|
}
|