11월 04일 원티드 프리온보딩 백엔드 10일차 TIL
in Wantedpreonbording on Wantedpreonbording, Django, Drf, Jwt
:one: 진행상황
- User 모델 및 API 설계
:two: 진행상황리뷰
# views.py
...
from rest_framework_simplejwt.tokens import RefreshToken
...
class LoginView(APIView):
def post(self, request: Request) -> Response:
...
token: Final[RefreshToken] = RefreshToken.for_user(user)
refresh: Final[RefreshToken] = str(token)
access: Final = str(token.access_token)
response = Response({"user": user.id})
response.set_cookie(key="refresh", value=refresh, httponly=True)
response.set_cookie(key="access", value=access, httponly=True)
return response
class LogoutView(APIView):
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
def post(self, request: Request) -> Response:
...
# get token from request cookie
refresh: Final = request.data.get("refresh")
...
try:
# delete token from database
token: Final = RefreshToken(refresh)
token.blacklist()
except Exception:
raise ParseError("Refresh token is invalid.")
# delete token from cookie
response = Response({"status": True})
response.delete_cookie("refresh")
response.delete_cookie("access")
return response
# tests.py
class TestUserView(APITestCase):
...
def test_logout(self):
...
# get tokens
refresh = logged_response.cookies["refresh"].value
access = logged_response.cookies["access"].value
# request logout with tokens
response: Final[Response] = self.client.post(
"/api/v1/users/logout/",
{"refresh": refresh},
HTTP_AUTHORIZATION=f"Bearer {access}",
)
- JWT 생성해 쿠키에 등록하여 로그인 인증하는 코드 작성.
:three: Today I Learned
JWT
- JSON Web Token
- 웹 상에서 로그인 인증을 위한 토큰을 JSON 형식으로 표현한 것
- 헤더(형식 및 알고리즘), 페이로드(내용), 서명(헤더와 페이로드를 합쳐 암호화한 것, 인증시 필요)으로 구성
djangorestframework-simplejwt
Django
와DRF
사용 시 JWT를 쉽게 사용할 수 있도록 도와주는 라이브러리- 설치:
pip install djangorestframework-simplejwt
- 설정
# config/settings.py INSTALLED_APPS = [ ... "rest_framework_simplejwt", 'rest_framework_simplejwt.token_blacklist', # 보안을 위해 블랙리스트 사용 ... ] ... EST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework_simplejwt.authentication.JWTAuthentication', ), } REST_USE_JWT = True SIMPLE_JWT = { 'ACCESS_TOKEN_LIFETIME': timedelta(hours=2), 'REFRESH_TOKEN_LIFETIME': timedelta(days=7), 'ROTATE_REFRESH_TOKENS': False, 'BLACKLIST_AFTER_ROTATION': True, 'TOKEN_USER_CLASS': 'user.User', }
- 사용
# views.py from rest_framework_simplejwt.tokens import RefreshToken # JWT 발급 View(로그인 등) class CreateJwtView(APIView): def post(self, request: Request) -> Response: ... # 유저로부터 refresh 토큰을 생성 token: Final[RefreshToken] = RefreshToken.for_user(user) # refresh 토큰을 문자열로 변환 refresh: Final[RefreshToken] = str(token) # refresh 토큰에서 access 토큰을 생성해 문자열로 변환 access: Final = str(token.access_token) # 응답 생성 response = Response({"data": data}) # 토큰을 쿠키에 등록 response.set_cookie(key="refresh", value=refresh, httponly=True) response.set_cookie(key="access", value=access, httponly=True) ... # 응답 반환 return response # JWT 인증이 필요한 View class CheckAuteView(APIView): # 인증 시 JWTAuthentication 사용 authentication_classes = [JWTAuthentication] permission_classes = [IsAuthenticated] # JWT 만료 View(로그아웃 등) class ExpireJwtView(APIView): ... def post(self, request: Request) -> Response: ... # 응답에서 refresh 토큰을 가져옴 refresh: Final = request.data.get("refresh") ... # refresh 토큰을 RefreshToken 객체로 변환 token: Final = RefreshToken(refresh) # 블랙리스트에 등록 token.blacklist() # 응답 생성 response = Response({"status": True}) # 쿠키에서 토큰 삭제 response.delete_cookie("refresh") response.delete_cookie("access") # 응답 반환 return response
- 테스트 시
- 실 사용시에는 아마 대부분 브라우저에서 처리해줄 듯
# tests.py class APITestView(APITestCase): # JWT 인증 필요한 View 테스트 ... def test_get_token(self): # JWT 발급 View 호출 response = self.client.post( # JWT 발급 URL, # JWT 발급 데이터, ) # 응답 쿠키에서 토큰 추출 self.refresh = logged_response.cookies["refresh"].value self.access = logged_response.cookies["access"].value def test_check_token(self): response: Final[Response] = self.client.post( # JWT 인증이 필요한 URL, # JWT 인증이 필요한 데이터, HTTP_AUTHORIZATION=f"Bearer {self.access}", ) # HTTP 인증 헤더에 access 토큰을 넣어서 요청
- 실 사용시에는 아마 대부분 브라우저에서 처리해줄 듯