Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ services:
volumes:
- .:/backend
# command : 컨테이너가 띄워질 때 실행하는 명령어 / 서버실행
# command: sh -c "python manage.py makemigrations && python manage.py migrate && python manage.py runserver 0.0.0.0:8000"
# command: sh -c "python manage.py makemigrations && python manage.py migrate && python manage.py runserver 0.0.0.0:8000"
command: "gunicorn gtd.wsgi --preload --bind 0.0.0.0:8000 --timeout 240"
restart: on-failure
ports:
Expand Down Expand Up @@ -60,6 +60,14 @@ services:
networks:
- gtd

redis:
image: "redis:alpine"
container_name: redis
ports:
- 6379:6379
networks:
- gtd

# prometheus:
# image: prom/prometheus
# volumes:
Expand Down
18 changes: 15 additions & 3 deletions gtd/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=60),
'REFRESH_TOKEN_LIFETIME': timedelta(days=7),
'ROTATE_REFRESH_TOKENS': False,
'BLACKLIST_AFTER_ROTATION': False,
'ROTATE_REFRESH_TOKENS': True,
'BLACKLIST_AFTER_ROTATION': True,
'UPDATE_LAST_LOGIN': False,

'ALGORITHM': 'HS256',
Expand Down Expand Up @@ -250,4 +250,16 @@
'PATCH',
'POST',
'PUT',
)
)

# redis
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}

4 changes: 3 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,6 @@ django-celery-results==2.5.1
gevent==23.9.1
openai==1.7.0
django-prometheus==2.3.1
gunicorn==21.2.0
gunicorn==21.2.0
redis==5.0.1
django-redis==5.4.0
2 changes: 1 addition & 1 deletion users/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
urlpatterns = [
path('register/', RegisterAPIView.as_view()), # 회원가입
path("auth/", AuthAPIView.as_view()), # post - 로그인, delete - 로그아웃, get - 유저정보
path('auth/refresh', TokenRefreshView.as_view()), # 토큰 재발급
path('auth/refresh/', TokenRefreshView.as_view()), # 토큰 재발급
]
55 changes: 42 additions & 13 deletions users/views.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from rest_framework import viewsets, status
from rest_framework.permissions import IsAuthenticated
# Create your views here.

import jwt

from gtd.settings import *
Expand All @@ -12,6 +12,8 @@

from django.contrib.auth import authenticate
from django.shortcuts import get_object_or_404
from django.core.cache import cache
from rest_framework_simplejwt.tokens import UntypedToken

# swagger 관련
from rest_framework.views import APIView
Expand All @@ -20,7 +22,6 @@

# from drf_yasg import openapi


class RegisterAPIView(APIView):
@swagger_auto_schema(request_body=SwaggerRegisterPostSerializer)
# 회원가입
Expand Down Expand Up @@ -86,54 +87,66 @@ def get(self, request):
serializer = SignSerializer(instance=user)
return Response(serializer.data, status=status.HTTP_200_OK)


except(jwt.exceptions.ExpiredSignatureError):
except jwt.exceptions.ExpiredSignatureError:
# 토큰 만료 시 토큰 갱신

data = {'refresh': request.COOKIES.get('refresh', None)}
serializer = TokenRefreshSerializer(data=data)

if serializer.is_valid(raise_exception=True):
access = serializer.data.get('access', None)
refresh = serializer.data.get('refresh', None)
payload = jwt.decode(access, SECRET_KEY, algorithms=['HS256'])
pk = payload.get('user_id')
user = get_object_or_404(User, pk=pk)
serializer = SignSerializer(instance=user)

res = Response(serializer.data, status=status.HTTP_200_OK)
res.set_cookie('access', access)
res.set_cookie('refresh', refresh)
return res

raise jwt.exceptions.InvalidTokenError

except(jwt.exceptions.InvalidTokenError):
except jwt.exceptions.InvalidTokenError:
# 사용 불가능한 토큰
return Response(status=status.HTTP_400_BAD_REQUEST)

@swagger_auto_schema(request_body=SwaggerLoginPostSerializer)
def put(self, request):
flag = request.data.get('flag')
key = request.data.get('key')

if flag == 'set':
value = request.data.get('value')
cache.set(key, value)
elif flag == 'get':
res = cache.get(key)
return Response({key : res}, status=status.HTTP_200_OK)

return Response({'msg': 'success'}, status=status.HTTP_200_OK)

# 로그인
def post(self, request):
# 유저 인증
user = authenticate(
email=request.data.get("email"), password=request.data.get("password")
)
if user is not None:
serializer = SignSerializer(user)
# 토큰 접근
token = TokenObtainPairSerializer.get_token(user)
refresh_token = str(token)
access_token = str(token.access_token)

# 리프레쉬 토큰을 Redis에 저장
cache.set(user.id, refresh_token, timeout=int(token.lifetime.total_seconds()))

res = Response(
{
# "user": serializer.data,
"message": "login success",
"token": {
"access": access_token,
"refresh": refresh_token,
},
}
},
status=status.HTTP_200_OK,
)
# 토큰, 쿠키에 저장
res.set_cookie("access", access_token, httponly=True)
res.set_cookie("refresh", refresh_token, httponly=True)
return res
Expand All @@ -142,10 +155,26 @@ def post(self, request):

# 로그아웃
def delete(self, request):
access_token = request.COOKIES.get('access')
refresh_token = request.COOKIES.get('refresh')
user = User.objects.filter(email=request.data.get("email")).first()

# access 토큰 검증
try:
UntypedToken(access_token)
except jwt.exceptions.InvalidTokenError:
return Response({'message': 'Invalid token.'}, status=status.HTTP_400_BAD_REQUEST)

# 쿠키에 저장된 토큰 삭제
response = Response({
"message": "Logout success"
}, status=status.HTTP_202_ACCEPTED)
response.delete_cookie("access")
response.delete_cookie("refresh")

# Redis에서 리프레쉬 토큰 삭제
cache.delete(user.id)

return response