Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ COPY requirements.txt .

RUN pip install --upgrade pip
RUN pip install -r requirements.txt
RUN pip install django-redis
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

requirements 에 적혀있는데 이거 빼도 되지 않나요??


# 소스 코드 복사
COPY . ./
Expand Down
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",
}
}
}

3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ 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
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()), # 토큰 재발급
]
53 changes: 42 additions & 11 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,68 @@ 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,
},
"user_id": user.id,
},
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 +157,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