- 添加用户登录、登出及令牌管理功能 - 实现基于JWT和Redis的认证系统 - 完善用户和角色管理API - 添加密码加密与验证功能 - 配置数据库连接和Redis客户端 - 实现中间件进行权限验证 - 初始化管理员和测试用户数据 - 更新模型和接口文档 - 添加测试API和配置文件
127 lines
4.6 KiB
Python
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 |