BookSystem/backend/auth/apps/users/urls.py
jayhgq f1da855793 feat(auth): 实现用户认证与授权功能
- 添加用户登录、登出及令牌管理功能
- 实现基于JWT和Redis的认证系统
- 完善用户和角色管理API
- 添加密码加密与验证功能
- 配置数据库连接和Redis客户端
- 实现中间件进行权限验证
- 初始化管理员和测试用户数据
- 更新模型和接口文档
- 添加测试API和配置文件
2026-02-28 18:08:53 +08:00

127 lines
4.6 KiB
Python

from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from fastcrud import FastCRUD
from datetime import timedelta
import redis.asyncio as redis
from database import get_db
from models import User
from schemas import UserCreate, UserResponse, UserLogin, Token
from utils.password import verify_password, get_password_hash
from utils.jwt import create_access_token
from config import app_settings
from utils.redis_client import get_redis, set_token_in_redis
from middleware import get_current_user
router = APIRouter(prefix="/users", tags=["users"])
# 创建FastCRUD实例
user_crud = FastCRUD(User, UserResponse)
@router.post("/login", response_model=Token)
async def login(user_data: UserLogin, db: AsyncSession = Depends(get_db), redis_conn: redis.Redis = Depends(get_redis)):
"""用户登录"""
# 查找用户
user = await db.query(User).filter(User.username == user_data.username).first()
# 验证用户是否存在且密码正确
if not user or not verify_password(user_data.password, user.password_hash):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="用户名或密码错误",
headers={"WWW-Authenticate": "Bearer"},
)
# 创建访问令牌
access_token_expires = timedelta(minutes=app_settings.access_token_expire_minutes)
access_token = create_access_token(
data={"sub": str(user.id), "username": user.username},
expires_delta=access_token_expires
)
# 将令牌存储到Redis
expire_seconds = int(access_token_expires.total_seconds())
await set_token_in_redis(redis_conn, user.id, access_token, expire_seconds)
return {"access_token": access_token, "token_type": "bearer"}
@router.post("/", response_model=UserResponse)
async def create_user(user_data: UserCreate, db: AsyncSession = Depends(get_db)):
"""创建用户"""
# 生成密码哈希值
hashed_password = get_password_hash(user_data.password)
# 创建用户实例,使用密码哈希值
db_user = User(
username=user_data.username,
nickname=user_data.nickname,
email=user_data.email,
phone=user_data.phone,
wx_openid=user_data.wx_openid,
avatar=user_data.avatar,
password_hash=hashed_password,
role_id=user_data.role_id
)
# 使用FastCRUD创建用户
created_user = await user_crud.create(db, db_user)
return created_user
@router.get("/", response_model=list[UserResponse])
async def get_users(db: AsyncSession = Depends(get_db), current_user: User = Depends(get_current_user)):
"""获取所有用户"""
users = await user_crud.get_multi(db)
return users
@router.get("/{user_id}", response_model=UserResponse)
async def get_user(user_id: int, db: AsyncSession = Depends(get_db), current_user: User = Depends(get_current_user)):
"""获取单个用户"""
user = await user_crud.get(db, user_id)
if not user:
raise HTTPException(status_code=404, detail="用户不存在")
return user
@router.put("/{user_id}", response_model=UserResponse)
async def update_user(user_id: int, user_data: UserCreate, db: AsyncSession = Depends(get_db), current_user: User = Depends(get_current_user)):
"""更新用户"""
# 生成密码哈希值
hashed_password = get_password_hash(user_data.password)
# 准备更新数据
update_data = user_data.dict()
update_data["password_hash"] = hashed_password
del update_data["password"] # 删除明文密码
# 使用FastCRUD更新用户
updated_user = await user_crud.update(db, user_id, update_data)
if not updated_user:
raise HTTPException(status_code=404, detail="用户不存在")
return updated_user
@router.delete("/{user_id}")
async def delete_user(user_id: int, db: AsyncSession = Depends(get_db), current_user: User = Depends(get_current_user)):
"""删除用户"""
deleted = await user_crud.delete(db, user_id)
if not deleted:
raise HTTPException(status_code=404, detail="用户不存在")
return {"message": "用户删除成功"}
@router.post("/logout")
async def logout(current_user: User = Depends(get_current_user), redis_conn: redis.Redis = Depends(get_redis)):
"""用户登出"""
from utils.redis_client import delete_token_from_redis
await delete_token_from_redis(redis_conn, current_user.id)
return {"message": "登出成功"}
@router.get("/me", response_model=UserResponse)
async def get_current_user_info(current_user: User = Depends(get_current_user)):
"""获取当前用户信息"""
return current_user