Skip to content

Latest commit

 

History

History
167 lines (116 loc) · 4.57 KB

Day17.md

File metadata and controls

167 lines (116 loc) · 4.57 KB

[Day17] OAuth2 實例: 密碼驗證

本次的程式碼與目錄結構可以參考 FastAPI Tutorial : Day17 branch

回顧

我們在 Day15Day16 完成 async CRUD 的實作
今天會開始進入 OAuth2 password login的實作
首先我們會先實作 密碼驗證

密碼驗證

關於在 DB 中儲存密碼

如果要在 DB 中儲存密碼,我們不能直接儲存明碼!
而是要將密碼進行加密後再儲存
常見的做法會是:

hash_password = hash_function(plain_password + salt)

這邊我們使用 bcrypt 密碼雜湊函數來進行加密


passlib 是一個密碼雜湊函數的套件,我們可以透過 passlib 來進行密碼的加密與驗證

poetry add passlib
poetry add bcrypt

新增 auth 目錄

mkdir auth
touch auth/passwd.py

調整 User Model

因為 passlib 使用 bcrypt 生成 hash password 的長度為 60,所以我們要調整 User model

models/base.py

class BaseType:
    # ...
    str_60 = Annotated[str, mapped_column(String(60))] # 新增 str_60
    # ...

models/user.py

# ...
class User(BaseType, Base):
    __tablename__ = "User"
    id:Mapped[BaseType.int_primary_key]
    password:Mapped[BaseType.str_60]
    # ...

因為我們要調整 User model,所以要先把 User table 刪除
可以直接進入 PostgreSQL 的 container 中,透過 psql 來刪除 table
也可以砍掉 PostgreSQL 的 container 和 對應的 volumn 再重新建立


透過 passlib hash 密碼、驗證密碼

接著在 auth/passwd.py 中撰寫 hash 與驗證密碼的 function


auth/passwd.py

from passlib.context import CryptContext

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

def verify_password(plain_password, hashed_password):
    return pwd_context.verify(plain_password, hashed_password)


def get_password_hash(password):
    return pwd_context.hash(password)

調整 create_user API endpoint

接著我們要調整 create_user API endpoint
把原本以明碼儲存的密碼,改成 hash 過的密碼存在 DB 中


routers/user.py 中的 create_user

from auth.passwd import get_password_hash

# ...

@router.post("/users" ,
        response_model=UserSchema.UserCreateResponse,
        status_code=status.HTTP_201_CREATED,
        response_description="Create new user"
)
async def create_user(newUser: UserSchema.UserCreate ):
    user = await UserCrud.get_user_id_by_email(newUser.email)
    if user:
        raise HTTPException(status_code=409, detail=f"User already exists")
    
    newUser.password = get_password_hash(newUser.password) # <--- 新增這行
    user = await UserCrud.create_user(newUser)
    return vars(user)

進入 container 中的 postgresql 檢查 user table

可以連進 PostgreSQL 的 container 中,透過 psql 來查看 user table 中
user 的 password 欄位是不是被 hash 過的密碼

docker exec -it fastapi_postgres_dev psql -U fastapi_tutorial

進入 PostgreSQL 後,可以順便檢查 SQLAlchemy 產生的 table schema 是否正確

```sql
\l -- list all databases
\c fastapi_tutorial -- connect to fastapi_tutorial database
\dt -- list all tables
\d "User" -- describe table "User"

docker exec psql


接著我們可以透過 SELECT 指令來查看 User table 中的資料

SELECT * FROM "User";

select users

可以看到 password 欄位中的密碼都是 hash 過的密碼 !

總結

今天我們透過 passlib 來進行密碼的 hash 與驗證
並且透過 bcrypt 來進行 hash
最後我們也透過 psql 來檢查 User table 中的密碼是否被 hash 過


接下來我們會繼續完成 OAuth2 password login 的實作
明天會接著講 fastapi.security 中提供與 OAuth2 相關的 schema !

Reference

FastAPI : OAuth2 with Password (and hashing), Bearer with JWT tokens