작성일: 2023년 10월 10일
소개
FastAPI는 Python으로 구축된 웹 프레임워크로, 비동기 프로그래밍과 데이터 검증을 지원하여 빠르고 효율적인 API를 개발할 수 있도록 도와줍니다. 이 글에서는 FastAPI를 사용하여 사용자 인증 및 권한 부여 기능을 구현하는 방법에 대해 자세히 설명하겠습니다.
1. 환경 설정
FastAPI와 관련 라이브러리를 설치하기 위해 Python의 패키지 관리자인 pip를 사용합니다. 다음 명령어를 통해 필요한 패키지를 설치합니다:
pip install fastapi uvicorn python-jose passlib[bcrypt]
각 라이브러리의 역할은 다음과 같습니다:
- fastapi: 웹 프레임워크
- uvicorn: ASGI 서버, FastAPI 애플리케이션을 실행하기 위해 사용
- python-jose: JWT(Json Web Token) 인코딩 및 디코딩을 위한 라이브러리
- passlib: 비밀번호 해싱을 위한 라이브러리
2. FastAPI 기본 구조
FastAPI 애플리케이션의 기본 구조는 다음과 같습니다:
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
위 코드를 실행하면 기본적인 FastAPI 서버가 생성됩니다. 이를 바탕으로 사용자 인증 및 권한 부여 기능을 추가해보겠습니다.
3. 사용자 모델 정의
사용자 모델을 정의하기 위해 Pydantic을 사용할 것입니다. Pydantic은 데이터 검증을 자동으로 처리해주는 라이브러리로, FastAPI와 잘 연동됩니다.
from pydantic import BaseModel, EmailStr
from typing import Optional
from passlib.context import CryptContext
class User(BaseModel):
username: str
email: EmailStr
full_name: Optional[str] = None
disabled: Optional[bool] = None
class UserInDB(User):
hashed_password: str
위의 User 모델은 사용자 정보를 정의하며, UserInDB는 데이터베이스에 저장되기 위해 해싱된 비밀번호를 포함합니다.
4. 비밀번호 해싱
비밀번호를 안전하게 저장하기 위해 해싱을 진행합니다. Passlib 라이브러리를 사용하여 비밀번호를 해싱하는 방법은 다음과 같습니다:
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def hash_password(password: str) -> str:
return pwd_context.hash(password)
해싱된 비밀번호는 사용자를 데이터베이스에 저장할 때 사용됩니다.
5. 사용자 등록 API
사용자 등록을 처리할 API 엔드포인트를 생성합니다. 사용자가 입력한 정보를 통해 새로운 사용자를 생성하고, 비밀번호는 해싱하여 저장합니다.
from fastapi import HTTPException, Depends
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
users_db = {}
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.post("/register/")
def register(user: User):
if user.username in users_db:
raise HTTPException(status_code=400, detail="Username already registered")
hashed_password = hash_password(user.password)
users_db[user.username] = UserInDB(
username=user.username, email=user.email, full_name=user.full_name, hashed_password=hashed_password
)
return {"msg": "User registered successfully!"}
6. JWT 생성 및 검증
JWT(Json Web Token)를 이용하여 사용자 인증 및 권한 부여를 수행합니다. 다음 코드에서는 JWT를 생성하고 검증하는 방법을 보여줍니다:
from datetime import datetime, timedelta
from jose import JWTError, jwt
SECRET_KEY = "mysecretkey"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
def verify_token(token: str):
credentials_exception = HTTPException(status_code=401, detail="Could not validate credentials")
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
except JWTError:
raise credentials_exception
return username
7. 로그인 API
사용자가 로그인할 수 있는 API를 구현합니다. 사용자는 사용자명과 비밀번호를 입력하여 JWT를 생성합니다.
@app.post("/token")
def login(form_data: OAuth2PasswordRequestForm = Depends()):
user = users_db.get(form_data.username)
if not user or not pwd_context.verify(form_data.password, user.hashed_password):
raise HTTPException(status_code=400, detail="Incorrect username or password")
access_token = create_access_token(data={"sub": user.username})
return {"access_token": access_token, "token_type": "bearer"}
8. 인증이 필요한 API 엔드포인트
인증이 필요한 엔드포인트를 추가하는 방법에 대해 설명합니다. OAuth2PasswordBearer 를 사용하여 API에 인증 기능을 추가할 수 있습니다.
from fastapi import Security
@app.get("/users/me", response_model=User)
def read_users_me(token: str = Depends(oauth2_scheme)):
username = verify_token(token)
user = users_db.get(username)
if user is None:
raise HTTPException(status_code=404, detail="User not found")
return user
9. 권한 부여 기능 추가하기
사용자 권한 부여 기능을 추가할 수 있습니다. 이를 통해 특정 사용자에게만 접근 허용하는 API 엔드포인트를 만들 수 있습니다.
def get_current_active_user(token: str = Depends(oauth2_scheme)):
username = verify_token(token)
user = users_db.get(username)
if user is None or user.disabled:
raise HTTPException(status_code=400, detail="Inactive user")
return user
@app.get("/users/me/role")
def read_user_role(current_user: User = Depends(get_current_active_user)):
return {"username": current_user.username, "role": "admin"}
10. FastAPI 서버 실행하기
FastAPI 서버를 실행하기 위해 uvicorn를 사용합니다. 다음 명령어로 서버를 실행할 수 있습니다:
uvicorn main:app --reload
서버가 실행되면, /docs 경로로 접속하여 Swagger UI를 통해 API를 테스트할 수 있습니다.
결론
FastAPI를 사용하여 사용자 인증 및 권한 부여 기능을 성공적으로 구현했습니다. FastAPI의 강력한 기능을 통해 효율적인 웹 API를 구축할 수 있으며, JWT를 활용한 인증 방식은 보안과 성능 모두를 고려할 수 있는 좋은 선택입니다. 앞으로 FastAPI의 다양한 기능을 탐색해 보시기 바랍니다.