Django ninja JWT를 활용하여 로그인 구현
Django ninja로 JWT를 구현하는 것은 쉽지가 않았다. 일단 레퍼런스가 많지 않을뿐더러 공식홈페이지를 보고 해보려 했지만 JWT와 관련해서는 친절하게 알려주지 않는다. 어렵게 구글링하고 공식홈페이지에 있는 코드를 접목시켜 header에 token을 저장하는 방법은 성공했지만 cookie에 저장하는 것은 아직 오류가 있다. 그래도 내가 시도한 곳 까지 블로그에 써보려한다.
일단 먼저 django-ninja-jwt 를 install 해준다.
여기에서는 poetry를 사용하기 떄문에 poetry add django-ninja-jwt 를 프로젝트 터미널 내에 입력해준다.
스키마
from ninja import Schema
class TokenSchema(Schema):
access: str
router를 관리하는 폴더를 만들어서 API를 여러 모듈로 쉽게 분할하는 방식으로 구현
상단에 router = Router(tags=["users"]) 코드를 넣어준다. (users 태그로 묶는다는 의미)
user_router.py
router = Router(tags=["users"])
@router.post(
"/sign_in",
auth=None,
response={200: TokenSchema},
)
def sign_in(request, username=Form(...), password=Form(...)):
try:
user_model = get_user_model().objects.get(username=username)
except get_user_model().DoesNotExist:
raise ValidationError
passwords_match = check_password(password, user_model.password)
if not passwords_match:
raise ValidationError
access = create_token(user_model.username)
return 200, TokenSchema(access=access)
create_token 함수는 services폴더에 정의했다.
services/auth_services.py
def create_token(username):
JWT_SIGNING_KEY = getattr(settings, "JWT_SIGNING_KEY", None)
JWT_ACCESS_EXPIRY = getattr(
settings, "JWT_ACCESS_EXPIRY", "15") # 15 minutes expiration
to_encode_access = {"sub": username}
access_expire = datetime.utcnow() + JWT_ACCESS_EXPIRY
to_encode_access.update({"exp": access_expire})
encoded_access_jwt = jwt.encode(
to_encode_access, JWT_SIGNING_KEY, algorithm="HS256")
return encoded_access_jwt
위 코드까지 작성하면 JWT 토큰을 받아서 리턴 할 수 있다.
getattr 함수를 이용하여 settings내에 JWT_SIGNING_KEY의 값을 JWT_SIGNING_KEY에 넣어준다.
여기서 JWT_SIGNING_KEY, JWT_ACCESS_EXPIRY는 settings에 정의한 변수이다.
settings.py
SECRET_KEY = env('SECRET_KEY') # SECRET_KEY를 env에 저장해놨다.
JWT_SIGNING_KEY = SECRET_KEY
JWT_ACCESS_EXPIRY = timedelta(minutes=5)
API 키는 클라이언트가 자신을 식별하기 위해 API를 호출할 때 제공하는 토큰인데 이것을 불러오기위해 아래와 같이 작성한다.
아래코드는 network header로 저장된다.
다시 user_router.py 에 돌아와서
class ApiKey(APIKeyHeader):
def authenticate(self, request, token):
try:
JWT_SIGNING_KEY = getattr(settings, "JWT_SIGNING_KEY", None)
payload = jwt.decode(token, JWT_SIGNING_KEY, algorithms=["HS256"])
username = payload.get("sub")
if username is None:
return None
except jwt.PyJWTError as e:
return None
return username
header_key = ApiKey()
@router.get("/headerkey", auth=header_key)
def apikey(request):
return f"username = {request.auth}"
인증받은 token을 decode하여 해당 token의 정보를 가져온다. (payload)
해당 token의 username을 불러와 return한다