fastapi项目 03-注册,密码加密

1. 前言

一般对于后端的接口,特别是注册接口而言,密码都不是明文存储的,而是通过加密的方式,存储加密后的密码的。

1.1 环境准备

我们需要下载第三方加密库:> pip install passlib
passlib 库里面会用到2个方法

  • encrypt() - 生成新的值,返回密码哈希
  • verify() - 根据现有哈希验证密码.
    Passlib是Python 2和3的密码散列库,它提供了30多种密码散列算法的跨平台实现,以及管理现有密码散列的框架。它被设计用于广泛的任务,从验证/etc/shadow中的散列,到为多用户应用程序提供全强度密码散列。
from passlib.hash import pbkdf2_sha256

password = "dack"  
hash_p = pbkdf2_sha256.hash("dack")  
print(f"加密后:{hash_p}")

我们多运行几次,发现每次得到的结果都不一样
加密后:$pbkdf2-sha256$29000$yBljbE3JGSPknHOu1VqrtQ$YN4k3VHR.lK1eUMrbrILP2JbIq0MkBArGvgCamaqG2c
加密后:$pbkdf2-sha256$29000$PkdIKcXYuxdizDkHIGQsRQ$XX5jghW9/Ez10fsIculxWX7PZ8A5upjx0cXs.2Wd7HE

这样就会让别人很难破解出你的原始密码内容,在数据库我们就可以保存加密后的值。当用户传一个密码值过来的时候,可以用verify() 方法验证密码是否正确。

# 验证密码
result1 = pbkdf2_sha256.verify("dack1", hash_p)
print(result1)
result2 = pbkdf2_sha256.verify("dack", hash_p)
print(result2)

# 运行结果
False
True

除了上面用到的pbkdf2_sha256 加密方式,它提供了30多种密码散列算法,使用方法都差不多,比如用sha256_crypt。

from passlib.hash import sha256_crypt

password = "dack"
hash_p = sha256_crypt.hash("dack")
print(f"加密后:{hash_p}")

# 验证密码
result1 = sha256_crypt.verify("dack1", hash_p)
print(result1)
result2 = sha256_crypt.verify("dack", hash_p)
print(result2)
# 运行结果:
加密后:$5$rounds=535000$BhJ3gP9aySdAmAdc$e5h9KJVtmmkhtT/W0RyoBsYrXChnC.aU7zLpaZV6X1B
False
True

2. models模型,对密码加密

# apps/model/models.py

    def hash_password(self, password: str):
        """密码加密"""
        self.password = sha256_crypt.encrypt(password)

    def verify_password(self, password: str):
        """校验密码"""
        return sha256_crypt.verify(password, self.password)

创建用户数据时,调用hash_password() 方法。

# apps/routers/view_login.py

from typing import Union
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel, Field, EmailStr

class UserInfo(BaseModel):
    username: str = Field(..., title="用户名", description="用户名", min_length=3, max_length=10)
    password: str = Field(..., title="密码", description="密码", min_length=6, max_length=16)
    email: Union[EmailStr, None] = Field(default=None, title="邮箱")

@router.post("/register")
async def register_demo(body: UserInfo, db: Session = Depends(get_db)):
    # db的实例
    print(f"请求body:{body}")
    user = User(**body.dict())
    if db.query(User).filter(User.username == user.username).count():
        # 已被注册
        raise HTTPException(status_code=400, detail="username already exist")
    # 密码加密
    user.hash_password(user.password)
    print(f'数据入库前:{user.username}')

    db.add(user)
    db.commit()
    db.refresh(user)
    return {"code": 0000, "body": user}

运行main.py文件。通过postman进行测试是否密码加密成功。

3. 封装crud,数据模型

上面都是将数据库的操作和数据的模型写在一个文件夹里面,这样会显示看着很乱。于是需要单独创建一个crud.py文件处理数据库的增删改查操作。创建schemas.py文件单独处理数据的模型。

"""数据库相关操作"""
# apps/model/crud.py

from apps.model.models import User
from fastapi import HTTPException

def is_user_exist(db, user):
    if db.query(User).filter_by(username=user.username).count() > 0:
        return True
    else:
        return False

def create_user(db, user):
    db_user = User(**user.dict())
    print(f'获取到user的数据模型:{db_user}')
    # 密码加密
    db_user.hash_password(user.password)
    print(f'数据入库前:{user.username}')
    try:
        db.add(db_user)
        db.commit()
        db.refresh(db_user)
    except Exception as e:
        print(f'数据写入失败:{e}')
        HTTPException(status_code=400, detail='用户注册失败!')
# apps/model/schemas.py

"""定义数据模型"""
from pydantic import BaseModel, Field, EmailStr
from typing import Optional

class UserIn(BaseModel):
    username: str = Field(..., title="用户名", description="用户名", min_length=3, max_length=10)
    password: str = Field(..., title="密码", description="密码", min_length=4, max_length=16)
    email: Optional[EmailStr] = None

在view接口函数中,对接口函数进行优化如下,这样的话,整体看起来就比较清晰有条理。


from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from apps.dependency import get_db
from apps.model import crud
from apps.model.schemas import UserIn

router = APIRouter()

@router.post("/login")
async def login_demo():
    return {"msg": "login success!"}

@router.post("/register")
async def register_demo(body: UserIn, db: Session = Depends(get_db)):
    # db的实例
    if crud.is_user_exist(db, body):
        # 已被注册
        raise HTTPException(status_code=400, detail="username already exist")
    # 注册用户写入到数据库中
    crud.create_user(db, body)
    return {"code": 0000, "msg": 'ok'}

posted @ 2023-12-28 14:04  dack_deng  阅读(215)  评论(0编辑  收藏  举报