企业级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表,之后再运行项目,运行成功之后可以看到密码已经加密了。

相关推荐
夏虫不可与之言冰1 小时前
Windows WSL2 (Ubuntu) 搭建 ESP‑IDF 6.1 开发环境全过程演示
linux·windows·ubuntu·esp32·乐鑫科技·乐鑫eim·乐鑫代理商
打工仔折腾 AI4 小时前
FaceFusion本地换脸实战:Windows整合包、模型选择与遮罩调参记录
人工智能·windows·后端·python·深度学习·性能优化·ai agent 实战
小蜗牛狂飙记6 小时前
Windows 双击 Jar 包无反应,不用写 bat 脚本!Jarfix 一键修复(以 Struts2 扫描工具为例)
windows·struts·jar
坐看风云变幻6 小时前
Betaflight 开发环境搭建保姆级教程(Ubuntu / Windows+WSL / Docker 三方案 + 官方网址大全)
windows·ubuntu·docker·无人机·飞控·betaflight
IT技术分享社区7 小时前
在 Windows 上跑未改动的 Linux 程序:微软 LiteBox v0.1 把这件事变简单了
linux·运维·windows·microsoft·开源
kakakahahahaha7 小时前
Win10/Win11 Windows Hello指纹不可用处理:WbioSrvc、传感器驱动与登录策略
windows·电脑·笔记本电脑·内容运营·软件需求
小黄人软件8 小时前
粘贴助手C++版 支持 Windows 7 / 10 / 11 不会出现缺少api-ms-win-core-path-11-1-0.dll
开发语言·c++·windows
Vex2une17 小时前
Windows 性能监视器发展史:从 PerfMon 计数器到 XPerf 深度诊断
windows·wpa·etw·性能监视器·perfmon·性能计数器·xperf
在角落发呆17 小时前
工具配置备份步骤:从手动备份到自动化方案
运维·windows·自动化
Vd7H20A718 小时前
Windows 下用 llama.cpp 运行 Qwen3.8-27B 并开启 512K 上下文 完全指南
windows·llama