""" 认证API路由示例代码 """ from fastapi import APIRouter, Depends, HTTPException, status from fastapi.security import OAuth2PasswordRequestForm from sqlalchemy.ext.asyncio import AsyncSession from app.api.deps import get_db from app.core.exceptions import UnauthorizedError, ConflictError from app.schemas.auth import UserRegister, Token, PasswordReset from app.schemas.base import ResponseModel from app.services.auth_service import AuthService router = APIRouter(prefix="/auth", tags=["认证"]) @router.post("/login", response_model=ResponseModel[Token], summary="用户登录") async def login( form_data: OAuth2PasswordRequestForm = Depends(), db: AsyncSession = Depends(get_db) ): """ 用户登录接口 - **username**: 用户名或邮箱 - **password**: 密码 返回访问令牌和刷新令牌 """ auth_service = AuthService(db) # 验证用户 user = await auth_service.authenticate_user( username=form_data.username, password=form_data.password ) if not user: raise UnauthorizedError("用户名或密码错误") # 创建Token token = await auth_service.create_tokens(user) return ResponseModel( code=200, message="登录成功", data=token ) @router.post("/register", response_model=ResponseModel[Token], status_code=status.HTTP_201_CREATED) async def register( user_data: UserRegister, db: AsyncSession = Depends(get_db) ): """ 用户注册接口 注册成功后自动登录,返回Token """ # 验证密码一致性 if user_data.password != user_data.confirm_password: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="两次输入的密码不一致" ) auth_service = AuthService(db) try: # 创建用户 user = await auth_service.create_user(user_data) # 自动登录 token = await auth_service.create_tokens(user) return ResponseModel( code=201, message="注册成功", data=token ) except ConflictError as e: raise HTTPException( status_code=status.HTTP_409_CONFLICT, detail=str(e) ) @router.post("/logout", response_model=ResponseModel) async def logout( token: str = Depends(get_current_token), db: AsyncSession = Depends(get_db) ): """ 用户登出接口 将当前Token加入黑名单 """ auth_service = AuthService(db) await auth_service.logout(token) return ResponseModel( code=200, message="登出成功" ) @router.post("/refresh", response_model=ResponseModel[Token]) async def refresh_token( refresh_token: str, db: AsyncSession = Depends(get_db) ): """ 刷新访问令牌 使用刷新令牌获取新的访问令牌 """ auth_service = AuthService(db) try: token = await auth_service.refresh_access_token(refresh_token) return ResponseModel( code=200, message="刷新成功", data=token ) except UnauthorizedError: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="刷新令牌无效或已过期" ) @router.post("/reset-password/request", response_model=ResponseModel) async def request_password_reset( email: str, db: AsyncSession = Depends(get_db) ): """ 请求重置密码 向用户邮箱发送重置链接 """ auth_service = AuthService(db) # 查找用户 user = await auth_service.get_user_by_email(email) if not user: # 为了安全,即使用户不存在也返回成功 return ResponseModel( code=200, message="如果邮箱存在,重置链接已发送" ) # 生成重置令牌 reset_token = await auth_service.create_password_reset_token(user) # TODO: 发送邮件 # await send_reset_email(email, reset_token) return ResponseModel( code=200, message="如果邮箱存在,重置链接已发送" ) @router.post("/reset-password/confirm", response_model=ResponseModel) async def reset_password( reset_data: PasswordReset, db: AsyncSession = Depends(get_db) ): """ 确认重置密码 使用重置令牌设置新密码 """ auth_service = AuthService(db) try: await auth_service.reset_password( token=reset_data.token, new_password=reset_data.new_password ) return ResponseModel( code=200, message="密码重置成功" ) except UnauthorizedError: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="重置令牌无效或已过期" ) # 辅助函数 async def get_current_token( authorization: str = Depends(oauth2_scheme) ) -> str: """获取当前请求的Token""" return authorization