Files
012-kaopeilian/docs/规划/后端开发拆分策略/子agent/01-Agent-Auth/examples/auth_service_example.py
111 998211c483 feat: 初始化考培练系统项目
- 从服务器拉取完整代码
- 按框架规范整理项目结构
- 配置 Drone CI 测试环境部署
- 包含后端(FastAPI)、前端(Vue3)、管理端

技术栈: Vue3 + TypeScript + FastAPI + MySQL
2026-01-24 19:33:28 +08:00

164 lines
5.0 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.
"""
认证服务示例代码
"""
from typing import Optional
from datetime import datetime, timedelta
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from fastapi import HTTPException, status
from app.core.security import verify_password, get_password_hash, create_access_token, create_refresh_token
from app.core.exceptions import UnauthorizedError, ConflictError, ForbiddenError
from app.core.logger import logger
from app.models.user import User
from app.schemas.auth import UserRegister, Token
class AuthService:
"""认证服务类"""
def __init__(self, db: AsyncSession):
self.db = db
async def authenticate_user(self, username: str, password: str) -> Optional[User]:
"""
验证用户身份
Args:
username: 用户名或邮箱
password: 密码
Returns:
验证成功返回User对象失败返回None
"""
# 查询用户(支持用户名或邮箱登录)
query = select(User).where(
(User.username == username) | (User.email == username)
)
result = await self.db.execute(query)
user = result.scalar_one_or_none()
if not user:
logger.warning("登录失败:用户不存在", username=username)
return None
# 验证密码
if not verify_password(password, user.password_hash):
logger.warning("登录失败:密码错误", user_id=user.id, username=username)
# TODO: 记录失败次数,实现账号锁定
return None
# 检查账号状态
if not user.is_active:
logger.warning("登录失败:账号已禁用", user_id=user.id, username=username)
raise ForbiddenError("账号已被禁用")
logger.info("用户登录成功", user_id=user.id, username=user.username)
return user
async def create_user(self, user_data: UserRegister) -> User:
"""
创建新用户
Args:
user_data: 用户注册数据
Returns:
创建的用户对象
Raises:
ConflictError: 用户名或邮箱已存在
"""
# 检查用户名是否存在
query = select(User).where(User.username == user_data.username)
result = await self.db.execute(query)
if result.scalar_one_or_none():
raise ConflictError("用户名已存在")
# 检查邮箱是否存在
query = select(User).where(User.email == user_data.email)
result = await self.db.execute(query)
if result.scalar_one_or_none():
raise ConflictError("邮箱已存在")
# 创建用户
user = User(
username=user_data.username,
email=user_data.email,
password_hash=get_password_hash(user_data.password),
is_active=True,
is_superuser=False,
role="trainee" # 默认角色为学员
)
self.db.add(user)
await self.db.commit()
await self.db.refresh(user)
logger.info("用户注册成功", user_id=user.id, username=user.username)
return user
async def create_tokens(self, user: User) -> Token:
"""
为用户创建访问令牌和刷新令牌
Args:
user: 用户对象
Returns:
Token对象
"""
# 创建访问令牌
access_token = create_access_token(
subject=user.id,
role=user.role,
username=user.username
)
# 创建刷新令牌
refresh_token = create_refresh_token(subject=user.id)
return Token(
access_token=access_token,
refresh_token=refresh_token,
token_type="bearer",
expires_in=1800, # 30分钟
user={
"id": user.id,
"username": user.username,
"email": user.email,
"role": user.role
}
)
async def logout(self, token: str) -> None:
"""
用户登出将token加入黑名单
Args:
token: 要失效的token
"""
# TODO: 将token加入Redis黑名单
# redis_key = f"blacklist:{token}"
# await redis.setex(redis_key, 3600, "1") # 设置1小时过期
logger.info("用户登出成功")
async def refresh_access_token(self, refresh_token: str) -> Token:
"""
使用刷新令牌获取新的访问令牌
Args:
refresh_token: 刷新令牌
Returns:
新的Token对象
"""
# TODO: 验证刷新令牌
# TODO: 检查是否在黑名单
# TODO: 生成新的访问令牌
# TODO: 可选 - 轮换刷新令牌
pass