TOP
본문 바로가기
[파이썬 Projects]/<파이썬 웹개발>

[파이썬] FastAPI - 메모 앱 프로젝트 3: 사용자 인증

by 기록자_Recordian 2025. 5. 9.
728x90
반응형
이전 내용
 

[파이썬] FastAPI - 메모 앱 프로젝트 2: CRUD 구현

이전 내용 [파이썬] FastAPI - 메모 앱 프로젝트 1: 초기 설정, DB 연동이전 내용 [파이썬] FastAPI - 성능 개선 팁이전 내용 [FastAPI] 미들웨어(middleware) - 3: GZip이전 내용 [FastAPI] 미들웨어(middleware) - 2: CORS

puppy-foot-it.tistory.com


PostgreSQL을 연동한 사용자 인증 구현
1. passlib 라이브러리

 

이번엔 FastAPI와 PostgreSQL 데이터베이스를 연동하여 사용자 인증 시스템을 구현한다. 이를 위해 passlib 라이브러리를 사용하여 비밀번호를 해시하고 검증한다.

※ passlib 라이브러리
https://pypi.org/project/passlib/

passlib는 비밀번호 해싱 및 검증을 위한 파이썬 라이브러리로, 사용자 인증 시스템에서 비밀번호 관리를 안전하고 쉽게 처리할 수 있도록 돕는다. 이 라이브러리는 다양한 해시 알고리즘을 지원하며, 사용자가 비밀번호를 안전하게 저장하고 검증할 수 있도록 설계되었다.
passlib는 데이터베이스 유형(MySQL, PostgreSQL 등)과 관계없이 비밀번호 해싱 및 검증을 위해 사용될 수 있다.

- 다양한 해시 알고리즘 지원: passlib는 BCrypt, PBKDF2, Argon2, SHA-256 등 여러 해시 알고리즘을 지원한다.
- 단순한 API: 비밀번호 해싱과 검증을 위한 간단한 메서드를 제공하므로, 사용자가 복잡한 구현 세부사항을 신경 쓸 필요가 없다.
- 자동 버전 관리: 해시 알고리즘이 변경되더라도, passlib는 비밀번호 해시를 자동으로 업데이트할 수 있는 메커니즘을 제공한다.
- 비밀번호 정책: 사용자가 생성하는 비밀번호의 안전성을 확인하기 위한 다양한 옵션을 지원한다.

 

passlib 라이브러리를 사용하기 위해 먼저 라이브러리를 설치한다. (pip 도 가능)

conda install passlib

 

설치가 완료되면 main.py 파일에 passlib 라이브러리를 사용하여 비밀번호를 해시하고 검증하는 코드를 작성해준다.

from passlib.context import CryptContext
# passlib을 사용한 사용자 인증
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

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

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

1. CryptContext 설정: pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

  • CryptContext 클래스: 비밀번호 해싱 및 검증을 위한 설정을 관리.
  • schemes=["bcrypt"]: bcrypt 해시 알고리즘을 사용하도록 지정. 이는 비밀번호 해싱에 강력한 방법이다.
  • deprecated="auto": 이 설정으로 알고리즘이 변경되더라도 이전 해시를 자동으로 감지하여 업데이트할 수 있다.

2. 비밀번호 해싱 함수: def get_password_hash(password):

  • 사용자가 입력한 평문 비밀번호를 받아 해싱하여 반환하는 함수.
  • pwd_context.hash(password): CryptContext의 hash 메서드를 통해 비밀번호를 안전하게 해싱.
  • 반환값은 해시된 비밀번호로, 데이터베이스에 저장될 수 있다.

3. 비밀번호 검증 함수: def verify_password(plain_password, hashed_password):

  • 사용자가 로그인할 때 입력한 비밀번호가 저장된 해시 비밀번호와 일치하는지 확인.
  • pwd_context.verify(plain_password, hashed_password): 두 비밀번호를 비교하여 일치 여부를 반환.
  • 반환값은 True(일치) 또는 False(불일치).
※해싱(hash)은 데이터를 고정된 길이의 문자열로 변환하는 과정으로, 주로 데이터의 무결성을 확인하거나, 비밀번호와 같은 민감한 정보를 안전하게 저장하기 위해 사용 된다.

PostgreSQL을 연동한 사용자 인증 구현
2. SQLAlchemy를 사용한 사용자 모델 정의

 

◆ 사용자 모델 정의

SQLAlchemy를 사용한 사용자 모델을 정의한다. 이 모델은 데이터베이스 테이블과 매핑된다.

# 사용자 모델 정의
class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True, index=True) # 정수형, PK
    username = Column(String(100), unique=True, index=True) # 중복 불가
    email = Column(String(200))
    hashed_password = Column(String(512))

# 회원 가입 시 데이터 검증
class UserCreate(BaseModel):
    username: str
    email: str
    password : str # 해시 전 패스워드

# 회원 로그인 시 데이터 검증
class UserLogin(BaseModel):
    username: str
    password: str # 해시 전 패스워드

 

◆ 세션 관리를 위한 미들웨어 추가

세션 관리를 위한 SessionMiddleware를 추가한다.

from starlette.middleware.sessions import SessionMiddleware

app = FastAPI()
app.add_middleware(SessionMiddleware, secret_key="your-secret-key")

 

해당 Middleware에 대한 설명은 아래 링크를 확인하면 된다.

 

[파이썬] FastAPI - 고급 인증: 세션

이전 내용 [FastAPI] 고급인증: JWT이전 내용 [FastAPI] 인증과 세션이전 내용 [FastAPI] SQLAlchemy와 CRUD (Depends, db.query)이전 내용 [파이썬] FastAPI - ORM 연동하기(SQLAlchemy)이전 내용 [파이썬] FastAPI - 웹소켓이

puppy-foot-it.tistory.com

 

◆ 사용자 정보 데이터베이스에 추가 기능 구현

데이터베이스에 사용자를 추가하는 기능을 구현하는데, 추가 이전에 비밀번호를 해시하도록 하는 기능도 같이 추가한다.

@app.post("/signup")
async def signup(signup_data: UserCreate, db: Session=Depends(get_db)):
    hashed_password = get_password_hash(signup_data.password) # 비밀번호 해시 기능
    new_user = User(username=signup_data.username, email=signup_data.email, hashed_password=hashed_password)
    db.add(new_user)
    db.commit()
    db.refresh(new_user)
    return {"message": "계정이 성공적으로 생성되었습니다.", "user_id": new_user.id}
  • /signup 경로로 들어온 POST 요청을 처리하며, 사용자가 입력한 비밀번호를 해싱하여 안전하게 저장한다.
  • 사용자 이름, 이메일, 해시된 비밀번호를 포함한 새 User 객체를 생성하여 데이터베이스에 저장하고 커밋한다.
  • 계정 생성 성공 시 메시지와 함께 새 사용자의 ID를 반환한다.
  • signup_data 매개변수는 UserCreate 모델을 통해 제공된 사용자 정보를 나타낸다.

◆ 로그인 기능 구현

데이터베이스를 조회하여 사용자 인증을 수행하는 로그인 엔드포인트와 세션을 기반으로 로그인 상태를 확인하는 대시보드 엔드포인트 구현

# 로그인
@app.post("/login")
async def login(request: Request, signin_data: UserLogin, db: Session=Depends(get_db)):
    user = db.query(User).filter(User.username == signin_data.username).first()
    if user and verify_password(signin_data.password, user.hashed_passwword):
        request.session["username"] = user.username
        return {"message": "로그인 성공!"}
    else:
        raise HTTPException(status_code=401, detail="유효하지 않은 정보 입니다.")
  • /login 경로로 들어온 로그인 요청(POST)을 처리하며, 사용자 이름을 기준으로 데이터베이스에서 해당 사용자를 조회한다.
  • 사용자가 존재하고, 입력한 비밀번호가 해시된 비밀번호와 일치하는지 확인한다.
  • 로그인에 성공하면 세션에 사용자 이름을 저장하고, 성공 메시지를 반환한다.
  • 사용자가 존재하지 않거나, 비밀번호가 일치하지 않을 경우 401 에러와 함께 오류 메시지를 반환한다.
  • signin_data 매개변수는 로그인 정보를 담고 있는 UserLogin 모델을 통해 가져온다.

◆ 로그아웃 기능 구현

# 로그아웃
@app.post("/logout")
async def logout(request: Request):
    request.session.pop("username", None)
    return {"message": "로그아웃 완료!"}
  • 로그아웃 시에는 현재 세션에서 사용자 정보를 제거(pop)하여 사용자를 로그아웃 상태로 만든다.
  • 세션 데이터는 Request 객체를 통해 관리된다.

사용자 인증 테스트

 

main.py를 실행하여 서버를 실행하고 POST MAN으로 테스트해 본다.

해당 서버를 실행하게 되면, 자동으로 users 라는 테이블이 생성된다.

◆ 회원가입

/signup 경로에서 JSON 형식으로 username, email, password를 입력 한 뒤, POST 요청을 하면 계정이 생성되었다는 메시지가 나온다.

 

계정이 잘 생성되어 데이터베이스에 삽입되었는지 확인해 보기 위해 SQL Shell에서 users 테이블을 조회해 본다.

SELECT * FROM users;

users 테이블 내에 사용자 정보가 잘 삽입된 것을 확인할 수 있다. 또한, password 값을 주었으나 앞서 passlib 라이브러리를 이용해 password 값을 받으면 비밀번호를 해시하는 함수를 생성하였기 때문에 해시된 비밀번호로 저장된 것을 확인할 수 있다.

 

◆ 로그인

/login 경로에서 JSON 형식으로 앞서 생성한 계정의 username과 password를 입력하여 POST 요청을 보내면 로그인이 성공했다는 메시지가 출력된다.

 

◆ 로그아웃

/logout 경로에서 POST 요청을 보내면 로그아웃이 완료되었다는 메시지가 출력된다.


[참고]

가장 빠른 풀스택을 위한 플라스크 & FastAPI

https://puppy-foot-it.tistory.com/828

https://pypi.org/project/passlib/

 


다음 내용

 

[파이썬] FastAPI - 메모 앱 프로젝트 4: 사용자별 메모 관리(feat. Almebic)

이전 내용 [파이썬] FastAPI - 메모 앱 프로젝트 3: 사용자 인증이전 내용 [파이썬] FastAPI - 메모 앱 프로젝트 2: CRUD 구현이전 내용 [파이썬] FastAPI - 메모 앱 프로젝트 1: 초기 설정, DB 연동이전 내용 [

puppy-foot-it.tistory.com

728x90
반응형