JWT(JSON Web Token)
쿠키 (Cookie)
- 웹 브라우저와 요청과 응답을 주고받을때 사용하는 데이터 조각이다.
- 도메인에 제한적(예시: naver.com에서 받은 쿠키는 naver.com에만 줄 수 있다)이다.
- 쿠키는 무제한이 아니며 유효기간이 정해져 있다.(유효기간이 지나면 자동으로 만료)
- Auth에만 사용되는 것이 아니라 다양한 방식으로 활용된다.(대표적으로 광고시장)
세션 (Session)
stateless한 HTTP 특징을 보완하기 위한 방법으로 서버에서 세션 DB를 만들어놓고 유저 정보를 기억해 세션 ID라고 하는 난수 값의 ID를 만들어서 쿠키에 담아서 클라이언트에 준다. 그렇게 클라이언트는 이제 세션 ID가 담긴 쿠키를 도메인에 줌으로써 유저정보를 기억하게 한다.
그래서 JWT란?
Cookie는 웹 브라우저에만 존재하는 것으로 다양한 장치들과 공통적으로 사용할 수 있는 방식이 필요하다. 이러한 방식으로 널리 사용되는 방법중 하나가 바로 Token Auth, 그중에서도 JWT 방식이 많이 사용된다.
Token은 간단히 말해 랜덤하게 생긴 문자열이며, 일정한 규칙을 가지고 있고 간단한 서명을 더한 문자열로 토큰 자체에 유저에 대한 간단한 정보가 들어있는 형태이다.
JWT 방식으로 Auth를 처리하면 Session DB나 인증을 위한 여러가지 로직 처리가 필요 없다.
처리방식 (JWT로 인증하는 Authentication방식)
- 클라이언트가 ID/PW를 서버로 보낸다.(로그인 시도)
- 서버에서 ID/PW를 검증하고 유효하다면 일정한 형식으로 서명 처리된 문자열 형식의 Token을 response 한다.
※ Token안에는 이미 유저 정보가 들어 있음 - 이후 클라이언트는 모든 요청 헤더에 토큰을 담아 서버로 요청을 전송한다.
- 서버는 해당 토큰의 유효성을 검증하고 유저의 신원과 권한을 확인 후 요청을 처리한다.
세션과의 차이
- 세션 데이터베이스가 존재하지 않으며 데이터베이스가 필요하지 않는다.
※ 토큰 자체가 하나의 인증 데이터다. - 서버는 토큰이 유효한지만 검증하여 처리한다.
장점과 단점
장점 : 서버에서 관리하는 데이터가 없으므로 복잡한 처리 로직이 필요하지 않고, 세션이나 DB없이 유저를 인증하는것이 가능하다.
단점 : 일방적으로 로그인을 무효화 하는 등의 처리가 불가능하고(모든 기기 로그아웃, 현재 접속 유저 관리 등), Token 자체가 데이터를 담고 있는 정보이므로 탈취당할시 보안이 취약하다.(민감한 정보는 담으면 안됨)
JWT 구조
HEADER : 토큰의 타입(jwt) 또는 서명 부분의 생성에 어떤 알고리즘(alg)이 사용되었는지 등을 저장한다.
PAYLOAD
- 토근 발급자, 토큰 대상자, 토큰 만료시간, 활성날짜, 발급시간 등등 여러 데이터가 담겨있는 부분으로 이곳에 서비스가 유저 정보를 담아서 인증한다.
- 누구나 풀어볼 수 있기 때문에 민감한 정보(user의 pk 값 등)를 담지않고 최소의 정보만 저장한다.
- 각각의 데이터는 Claim이라고도 하며 Key-Value 형태로 구성된다.
SIGNATURE(서명)
- HEADER + PAYLOAD + 서버의 비밀키 값을 HEADER에 명시된 암호 알고리즘 방식으로 생성한 값이다.
- 즉 PAYLOAD의 글자 하나만 달라져도 SIGNATURE는 완전히 다른 문자열로 변환되어 서버의 비밀키 값을 모른다면 유효한 서명값을 임의로 만들어내는 것이 불가능하다.
- 서버는 토큰을 받으면 HEADER + PAYLOAD + 비밀키로 생성한 서명값이 토큰의 서명값과 일치하는지를 확인하는 과정을 거쳐서 유효성 여부를 확인한다.
- 서명의 유효여부 + 유효기간 내의 토큰인지(만료된 토큰은 아닌지) 확인하여 Auth 과정을 처리한다.
Access Token과 Refresh Token
JWT 인증 방식은 굉장히 장점이 많은데 단점도 확실하기 때문에 어떻게 보완할 수 있을까를 생각하다가 나온 결론은
토큰의 유효시간을 매우 짧게 잡아버리자는 것이다. (탈취당해도 금방 만료되게끔)
Access Token
- 요청할 때 인증을 위해 헤더에 포함해야하는 토큰으로 매 요청시 보내는 토큰이므로 보안이 취약하다.
- 그래서, 만료 기한을 짧게 잡아 만약 탈취 당해도 짧은 시간안에 유효하지 않은 토큰 형태로 만든다.
Refresh Token
- Access Token이 만료되었을때 새로 Access Token을 발급받기 위한 Token이다.
- Access Token이 만료되었을때 사용하기 때문에 Access Token 보다 긴 유효기간을 가진다.
- 주로 사용자의 기기에 저장해두고 사용되며 만약 Refresh Token까지 만료되었다면 다시 인증(로그인) 과정이 필요하다.
- Refresh Token의 탈취를 보완하기 위해 DB 리소스를 사용하는 다양한 방식도 존재한다. (BlackList 등)
JWT 구현하기
설치
pip install djangorestframework-simplejwt
설정
# settings.py (AUTH 밑에 둘거임)
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework_simplejwt.authentication.JWTAuthentication",
],
}
사용
# accounts/urls.py
from django.urls import path
from rest_framework_simplejwt.views import (
TokenObtainPairView,
TokenRefreshView,
)
app_name = "accounts"
urlpatterns = [
path("signin/", TokenObtainPairView.as_view(), name="token_obtain_pair"),
path("token/refresh/", TokenRefreshView.as_view(), name="token_refresh"),
]
로그인 이후에 토큰이 발급되는 것을 확인할 수 있다.
JWT Settings
from datetime import timedelta
...
SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=5), # access_token 유효기간
"REFRESH_TOKEN_LIFETIME": timedelta(days=1), # refresh_token 유효기간
"ROTATE_REFRESH_TOKENS": True, # True면 access_token 재발행시 refresh_token도 재발행
"BLACKLIST_AFTER_ROTATION": True, # 전에 쓰던 refresh_token 블랙리스트 등록
}
이외에도 다양한 설정이 있으니 필요한 기능이 있다면
=> https://django-rest-framework-simplejwt.readthedocs.io/en/latest/settings.html
접근 제한하기
웹 서비스를 이용하다보면 로그인 또는 회원가입을 한 뒤 서비스를 이용할 수 있는 것들이 많다.
유저가 아니면 Article 기능 접근 제한
# views.py
from rest_framework.permissions import IsAuthenticated
...
class ArticleListAPIView(APIView):
permission_classes=[
IsAuthenticated
]
def get(self, request):
articles = Article.objects.all()
serializer = ArticleSerializer(articles, many=True)
return Response(serializer.data)
def post(self, request):
serializer = ArticleSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
를 이용해서 인증된 사용자만 서비스를 이용할 수 있게 할 수 있다.
Bearer : JWT 혹은 OAuth에 대한 토큰임을 명시하는 것으로(RFC 6750) 이 값도 특정 단어로 변경할 수 있으나 하지말자.
'웹 개발' 카테고리의 다른 글
[Django] DRF Project - 2 (0) | 2024.09.05 |
---|---|
[Django] DRF Project - 1 (0) | 2024.09.03 |
[Django] 심화 과정에 들어가기 전 개념 (0) | 2024.08.29 |
[Django] URL Namespace (0) | 2024.08.29 |
[Django] Django form (0) | 2024.08.28 |