오늘은 지난 시간에 이어 TodoProject 를 진행할 것이다.
지난 시간에는 유저 생성 시 비밀번호 해싱, db 에 add 하는 방법을 배웠다.
이번 시간에는 토큰 기반 인증을 배워볼 것이다.
우선, 가상환경 내부에서 python-multipart 를 설치한다.
pip install python-multipart
python-multipart 는 python 에서 multipart/form-data 파싱을 처리하기 위한 라이브러리이다.
이 라이브러리는 주로 파일 업로드 및 다중 파트 폼 데이터를 수신할 때 사용된다.
그리고 OAuth2PasswordRequestForm 을 사용할 것이다. 이는 fastapi에서 제공하는 클래스로 OAuth2의 패스워드 그랜트 유형에 따라 사용자 인증을 처리하는 데 사용된다.
이 폼을 사용하면 사용자로부터 username 과 password 를 안전하게 받아서 OAuth2 토큰을 발급할 수 있다.
OAuth2PasswordRequestForm 는 1. 사용자의 로그인 요청을 처리하고, 검증하여 유효한 경우 2. 액세스 토큰을 발급한다.
토큰은 쉽게 비유하자면, 놀이공원에 가면 입장권이 필요하듯이 컴퓨터나 핸드폰에서 특별한 곳에 들어가기 위한 '입장권'같은 것이다.
만약 게임을 할 때 특별한 비밀번호를 받게되는 경우가 있다. 우리는 이런 걸 토큰이라하고 이 토큰이 있어야 게임을 할 수가 있다.
다시 돌아와서, 토큰은 사용자나 시스템이 서로를 식별하고 정보에 접근하는 것을 안전하게 하기 위해 사용된다.
토큰을 사용하면 다음의 이점이 있다.
from fastapi import APIRouter, Depends
from pydantic import BaseModel
from sqlalchemy.orm import Session
from starlette import status
from typing import Annotated
from database import SessionLocal
from models import Users
from passlib.context import CryptContext
from fastapi.security import OAuth2PasswordRequestForm
router = APIRouter()
bcrypt_context = CryptContext(schemes=['bcrypt'], deprecated='auto')
#pydantic
class CreateUserRequest(BaseModel):
username: str
email: str
first_name: str
last_name : str
password: str
role: str
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
db_dependency = Annotated[Session, Depends(get_db)]
def authenticaate_user(username: str, password: str, db):
user = db.query(Users).filter(Users.username == username).first()
if not user:
return False
if not bcrypt_context.verify(password, user.hashed_password):
return False
return True
@router.post("/auth", status_code =status.HTTP_201_CREATED)
async def create_user(db: db_dependency,
create_user_request: CreateUserRequest):
create_user_model = Users(
email=create_user_request.email,
usedbrname=create_user_request.username,
first_name=create_user_request.first_name,
last_name=create_user_request.last_name,
role=create_user_request.role,
# 비밀 번호는 해싱되어야함.
hashed_password=bcrypt_context.hash(create_user_request.password),
is_active=True
)
db.add(create_user_model)
db.commit()
@router.post("/token")
async def login_for_access_token(form_data: Annotated[OAuth2PasswordRequestForm, Depends()],
db: db_dependency):
user = authenticaate_user(form_data.username, form_data.password, db)
if not user:
return 'Failed Authentication'
return 'Successful Authentication'
# 인증. 라우터 패키지 안에 존재
@router.get("/auth")
async def get_user():
return {'user': 'authenticated'}