企业级FastAPI后端模板搭建(六)加密用户密码

查看sys_user表(如下图),可以发现密码是明文,安全性不高,所以需要进行加密。

封装数据库CURD

新建 core/crud.py 文件:

python 复制代码
from typing import Any, Generic, NewType, TypeVar
from tortoise.models import Model
from pydantic import BaseModel
from tortoise.expressions import Q

ModelType = TypeVar("ModelType", bound=Model)
CreateSchemaType = TypeVar("CreateSchemaType", bound=BaseModel)
UpdateSchemaType = TypeVar("UpdateSchemaType", bound=BaseModel)
Total = NewType("Total", int)

class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]):
    def __init__(self, model: type[ModelType]):
        self.model = model

    async def get(self, id: int) -> ModelType:
        """根据ID获取单个记录"""
        return await self.model.get(id=id)
    
    async def list(self, page: int, page_size: int, search: Q = Q(), order: list | None = None) -> tuple[Total, list[ModelType]]:
        """分页查询记录列表"""
        query = self.model.filter(search)
        if order is None:
            order = []
        total = await query.count()
        items = await query.offset((page - 1) * page_size).limit(page_size).order_by(*order)
        return total, items
    
    async def create(self, obj_in: CreateSchemaType) -> ModelType:
        """创建新记录"""
        if isinstance(obj_in, dict):
            obj_dict = obj_in
        else:
            obj_dict = obj_in.model_dump()
        obj = self.model(**obj_dict)
        await obj.save()
        return obj
    
    async def update(self, id: int, obj_in: UpdateSchemaType | dict[str, Any]) -> ModelType:
        """更新指定记录"""
        if isinstance(obj_in, dict):
            obj_dict = obj_in
        else:
            obj_dict = obj_in.model_dump(exclude_unset=True, exclude={"id"})
        obj = await self.get(id=id)
        obj = obj.update_from_dict(obj_dict)
        await obj.save()
        return obj
    
    async def remove(self, id: int) -> None:
        """删除单个记录"""
        obj = await self.get(id=id)
        await obj.delete()

    async def bulk_remove(self, ids: str) -> None:
        """批量删除记录"""
        id_list = ids.split(",")
        await self.model.filter(id__in=id_list).delete()

封装密码加密

安装依赖

首先安装 passlib 库:

shell 复制代码
pip install passlib[bcrypt] setuptools argon2-cffi

实现密码工具

新建 utils/crud.py 文件:

python 复制代码
from passlib import pwd
from passlib.context import CryptContext

# 使用 Argon2 算法创建密码上下文
pwd_context = CryptContext(schemes=["argon2"], deprecated="auto")

def verify_password(plain_password: str, hashed_password: str) -> bool:
    """验证明文密码与哈希密码是否匹配"""
    return pwd_context.verify(plain_password, hashed_password)

def get_password_hash(password: str) -> str:
    """生成密码哈希值"""
    return pwd_context.hash(password)

def generate_password() -> str:
    """生成随机密码"""
    return pwd.genword()

创建UserRepository

安装依赖

shell 复制代码
pip install pydantic[email]

创建Schemas

新建 schemas/users.py 文件:

python 复制代码
from datetime import datetime
from pydantic import BaseModel, EmailStr, Field

class BaseUser(BaseModel):
  id: int
  account: str | None = None
  username: str | None = None
  email: EmailStr | None = None
  is_active: bool | None = True
  is_superuser: bool | None = False
  created_at: datetime | None
  updated_at: datetime | None
  last_login: datetime | None
  roles: list | None = []

class UserCreate(BaseModel):
  account: str = Field(example="admin")
  username: str = Field('', example="Admin1")
  gender: int | None = Field(0, description="性别")
  password: str = Field(
    default="admin1234",
    example="AdminPass123",
    min_length=8,
    description="密码(至少8位,包含字母和数字)",
  )
  avatar: str | None = Field('', description="用户头像")
  mobile: str = Field(None, description="联系方式")
  email: EmailStr = Field('', example="admin@qq.com")
  status: int | None = Field(1, description="状态")
  is_active: bool | None = True
  is_superuser: bool | None = False
  dept_id: int | None = Field(0, description="部门ID")
  create_by: int | None = Field(None, description="创建人")
  update_by: int | None = Field(None, description="修改人")

class UserUpdate(BaseModel):
  id: int
  account: str
  username: str = Field('', example="Admin1")
  gender: int | None = Field(0, description="性别")
  avatar: str | None = Field('', description="用户头像")
  mobile: str | None = Field('', description="联系方式")
  email: str | None = Field('', example="admin@qq.com")
  status: int | None = Field(1, description="状态")
  is_active: bool | None = True
  is_superuser: bool | None = False
  role_ids: list[int] | None = []
  dept_id: int | None = 0
  update_by: int | None = Field(None, description="修改人")

创建Repository

新建 repositories/user.py 文件:

python 复制代码
from core.crud import CRUDBase
from models.admin import User
from schemas.users import UserCreate, UserUpdate
from utils.password import get_password_hash

class UserRepository(CRUDBase[User, UserCreate, UserUpdate]):
  def __init__(self):
    super().__init__(model=User)

  async def create_user(self, obj_in: UserCreate) -> User:
    obj_in.password = get_password_hash(password=obj_in.password)
    obj = await self.create(obj_in)
    return obj

user_repository = UserRepository()

修改初始化文件

修改core/insert_data.py文件:

python 复制代码
from repositories.user import UserCreate, user_repository

...
async def init_superuser():
  logger.info("🔧 开始初始化超级管理员用户...")
  user = await user_repository.model.exists()
  if not user:
    await user_repository.create_user(
      UserCreate(
        account="admin",
        username="超级管理员",
        email="admin@admin.com",
        password="abcd1234",
        dept_id=1,
        is_active=True,
        is_superuser=True,
      )
    )
    logger.info("✅ 超级管理员用户创建成功 - 用户名: admin")
  else:
    logger.info("ℹ️ 超级管理员用户已存在,跳过创建")

先清空sys_user表,之后再运行项目,运行成功之后可以看到密码已经加密了。

相关推荐
脚踏实地皮皮晨7 分钟前
003002004_WPF Panel 基类 官方类定义
开发语言·windows·算法·c#·wpf·visual studio
IT小盘10 小时前
12-Prompt不等于一句话-System-User-Context三层结构
java·windows·prompt
PieroPc15 小时前
Python 写的 Windows凭据添加工具 第二版 解决Win11 23H2, 25H2,26H1,共享文件和打印 一部份问题,不是全部!
开发语言·windows·python
feasibility.17 小时前
wsl安装Ubuntu方法(含网络不稳定处理)
linux·运维·windows·ubuntu
weixin_4713830317 小时前
07 FastAPI
python·fastapi
我命由我1234518 小时前
Windows 操作系统 - 符号链接
linux·运维·windows·系统架构·操作系统·运维开发·系统
还是大剑师兰特19 小时前
ThinkPad T490 Windows 自动修复失败完整解决步骤
windows·大剑师·t490
小白说大模型20 小时前
KMP全栈开发:从Android到AI Agent的技术演进与实践
android·java·人工智能·windows·python
带娃的IT创业者21 小时前
LG 显示器通过 Windows Update “静默”安装软件:一场关于数字主权的无声博弈
windows·计算机外设·隐私安全·驱动程序·windows update·数字主权·lg显示器
2501_936415691 天前
Stream流和方法引用
windows·microsoft