์๊ธฐ๋ฅ #50
์งํ์ค์๊ธฐ๋ฅ #44: ๐ Mangement Cloud server FastAPI ํ๋ก์ ํธ
๐ฅ Epic 1 - Task (Login) ๐ FastAPI ๋ก๊ทธ์ธ ๊ธฐ๋ฅ ์ค๊ณ ๋ฐ ๊ตฌํ
0%
์ค๋ช
- ๋ชฉ์ฐจ
- ๐ฅ Epic 1 โ Task (Login)
- ๐ FastAPI ๋ก๊ทธ์ธ ๊ธฐ๋ฅ ์ค๊ณ ๋ฐ ๊ตฌํ
- ๐ ์์ ๊ฐ์
- ๐ฏ ์์ ๋ชฉ์
- ๐ ๏ธ ์งํ ๋ด์ฉ
- ๐๏ธ 1. ์ฌ์ฉ์ ๋ชจ๋ธ ์์ฑ (app/models/user.py)
- ๐๏ธ 2. ๋น๋ฐ๋ฒํธ ํด์ ์ ํธ ์์ฑ (app/core/security.py)
- ๐๏ธ 3. JWT ํ ํฐ ๋ฐ๊ธ ๊ธฐ๋ฅ ์์ฑ (app/core/jwt.py)
- ๐๏ธ 4. ๋ก๊ทธ์ธ API ์์ฑ (app/routers/auth.py)
- ๐๏ธ 5. main.py์ ๋ผ์ฐํฐ ๋ฑ๋ก
- ๐๏ธ 6. test_main.http๋ก ๋ก๊ทธ์ธ ํ ์คํธ
- ๐ ์ฐ์ถ๋ฌผ
- ๐ FastAPI ๋ก๊ทธ์ธ ๊ธฐ๋ฅ ์ค๊ณ ๋ฐ ๊ตฌํ
๐ฅ Epic 1 โ Task (Login)¶
๐ FastAPI ๋ก๊ทธ์ธ ๊ธฐ๋ฅ ์ค๊ณ ๋ฐ ๊ตฌํ¶
๐ ์์ ๊ฐ์¶
mgmt ์๋ฒ์ ์ ๊ทผํ๋ ๋ชจ๋ ๊ธฐ๋ฅ(์๋ฒ ์ํ ์กฐํ, ์ ์ด, reverse SSH ๋ฑ)์
์ธ์ฆ๋ ์ฌ์ฉ์๋ง ์ฌ์ฉํ ์ ์์ด์ผ ํ๋ค.
๋ฐ๋ผ์ FastAPI ๊ธฐ๋ฐ์ JWT ๋ก๊ทธ์ธ ์์คํ ์ ๊ตฌ์ถํ์ฌ
API ์ ๊ทผ์ ์์ ํ๊ฒ ๋ณดํธํ๋ ์ธ์ฆ ๋ ์ด์ด๋ฅผ ๋ง๋ ๋ค.
์ด ์์ ์ ์ ์ฒด ์์คํ ์ ๋ณด์ ๊ธฐ๋ฐ์ด ๋๋ ํต์ฌ ๋จ๊ณ์ด๋ค.
๐ฏ ์์ ๋ชฉ์ ¶
-
FastAPI์์ JWT ๊ธฐ๋ฐ ๋ก๊ทธ์ธ ๊ธฐ๋ฅ์ ๊ตฌํํ๋ค.
-
/login์๋ํฌ์ธํธ๋ฅผ ์์ฑํ์ฌ ์ฌ์ฉ์ ์ธ์ฆ์ ์ฒ๋ฆฌํ๋ค. -
๋น๋ฐ๋ฒํธ๋ ํด์(SHA256 ๋๋ bcrypt)๋ก ์ ์ฅํ๋ค.
-
๋ก๊ทธ์ธ ์ฑ๊ณต ์ Access Token(JWT)์ ๋ฐ๊ธํ๋ค.
-
์ดํ ๋ชจ๋ API์์ Authorization: Bearer ๋ฐฉ์์ผ๋ก ์ธ์ฆ์ ์ ์ฉํ๋ค.
๐ ๏ธ ์งํ ๋ด์ฉ¶
๐๏ธ 1. ์ฌ์ฉ์ ๋ชจ๋ธ ์์ฑ (app/models/user.py)¶
๋ก๊ทธ์ธ ๊ธฐ๋ฅ์ ์ํด ์ฌ์ฉ์ ํ ์ด๋ธ์ ์์ฑํ๋ค.
# app/models/user.py
from sqlalchemy import Column, String
from app.db import Base
class User(Base):
__tablename__ = "user"
username = Column(String(50), primary_key=True)
password_hash = Column(String(200))
๐ ์ค๊ณ ํฌ์ธํธ
-
๋น๋ฐ๋ฒํธ๋ ์ ๋ ํ๋ฌธ ์ ์ฅ ๊ธ์ง
-
username์ PK๋ก ์ค์ ํ์ฌ ๊ฐ๋จํ ์ธ์ฆ ๊ตฌ์กฐ ์ ์ง
๐๏ธ 2. ๋น๋ฐ๋ฒํธ ํด์ ์ ํธ ์์ฑ (app/core/security.py)¶
# app/core/security.py
import hashlib
def hash_password(password: str) -> str:
return hashlib.sha256(password.encode()).hexdigest()
def verify_password(password: str, hashed: str) -> bool:
return hash_password(password) == hashed
๐ bcrypt๋ก ๋ฐ๊ฟ ์๋ ์์ง๋ง
Park mgmt ์๋ฒ๋ ๋ด๋ถ์ฉ์ด๋ผ SHA256๋ ์ถฉ๋ถํจ.
๐๏ธ 3. JWT ํ ํฐ ๋ฐ๊ธ ๊ธฐ๋ฅ ์์ฑ (app/core/jwt.py)¶
# app/core/jwt.py
from datetime import datetime, timedelta
from jose import jwt
SECRET_KEY = "park-secret-key"
ALGORITHM = "HS256"
def create_access_token(username: str):
expire = datetime.utcnow() + timedelta(hours=12)
payload = {"sub": username, "exp": expire}
return jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM)
๐ 12์๊ฐ ํ ํฐ
๐ HS256
๐ jose ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉ
๐๏ธ 4. ๋ก๊ทธ์ธ API ์์ฑ (app/routers/auth.py)¶
# app/routers/auth.py
from fastapi import APIRouter, HTTPException, Depends
from sqlalchemy.orm import Session
from app.db import SessionLocal
from app.models.user import User
from app.core.security import verify_password
from app.core.jwt import create_access_token
router = APIRouter()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@router.post("/login")
def login(username: str, password: str, db: Session = Depends(get_db)):
user = db.query(User).filter(User.username == username).first()
if not user or not verify_password(password, user.password_hash):
raise HTTPException(status_code=401, detail="Invalid credentials")
token = create_access_token(username)
return {"access_token": token, "token_type": "bearer"}
๐๏ธ 5. main.py์ ๋ผ์ฐํฐ ๋ฑ๋ก¶
from fastapi import FastAPI
from app.routers import auth
app = FastAPI()
app.include_router(auth.router)
๐๏ธ 6. test_main.http๋ก ๋ก๊ทธ์ธ ํ ์คํธ¶
### Login Test
POST http://127.0.0.1:8000/login
Content-Type: application/json
{
"username": "admin",
"password": "1234"
}
๐ ์ฐ์ถ๋ฌผ¶
-
์ฌ์ฉ์ ๋ชจ๋ธ ์์ฑ (user.py)
-
๋น๋ฐ๋ฒํธ ํด์ ๊ธฐ๋ฅ (security.py)
-
JWT ๋ฐ๊ธ ๊ธฐ๋ฅ (jwt.py)
-
/login API ๊ตฌํ
-
test_main.http๋ก ๋ก๊ทธ์ธ ํ ์คํธ ์๋ฃ
-
์ธ์ฆ ๊ธฐ๋ฐ ๊ตฌ์ถ ์๋ฃ
ํ์ํ ๋ฐ์ดํฐ๊ฐ ์์ต๋๋ค.