참고자료(사실상 이거 복붙)
https://medium.com/@abdul45.wajid/jwt-authentication-in-django-channels-efe9404f2fc7
JWT Authentication in Django Channels
Package Installation
medium.com
django channels 공식 홈페이지
https://channels.readthedocs.io/en/latest/tutorial/index.html

django channels 란
- django에서 비동기(Asynchronous) 처리를 가능하게 해주는 라이브러리
원래 django는 WSGI(Web Server Gateway Interface)를 사용
channels 의 흐름
- 클라이언트에서 request(요청) 을 보내면 Protocol Router에서 단순 http 요청인지 websocket 요청인지 판단 -> 만약에 http 요청일 경우 views.py로 전달되서 기존 django 동작으로 진행 / 아닐경우 Consumer (views.py 와 같은역할) 에 전달되서 websocket 으로 동작이 진행
Websocket 주요 구성 파일
- consumers.py : WebSocket 요청을 처리해준다(== views.py)
- routing.py : WebSocket URL과 Consumer를 연결해준다(==urls.py)
- asgi.py : Protocol Router의 개념(여기에서 미들웨어처리)
Http와 Websocket 차이점
- HTTP는 클라이언트가 서버에 요청(request)을 보내면, 서버는 클라이언트에 응답(response)을 보내고 연결을 끊는 형식으로 작동
- WebSocket 프로토콜은 TCP를 기반으로하는 양방향 통신을 지원하는 프로토콜입니다. 클라이언트와 서버 간의 연결을 한번 맺으면, 연결을 유지하면서 양방향으로 데이터를 주고받을 수 있음
기본세팅
- channels 공식홈페이지 tutorial 부분 참고
channels에서 jwt token을 받고 연결하는 방법
- 기본적으로는 channels 에서는 세션(AuthMiddlewareStack)을 이용한 인증하는 방법을 제공한다.
- 그래서 jwt token을 이용해 로그인방식을 사용한 경우 따로 커스터마이징이 필요
"""chat/middleware.py"""
from urllib.parse import parse_qs
from channels.auth import AuthMiddlewareStack
from channels.db import database_sync_to_async
from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser
from django.db import close_old_connections
from jwt import InvalidSignatureError, ExpiredSignatureError, DecodeError
from jwt import decode as jwt_decode
User = get_user_model()
class JWTAuthMiddleware:
def __init__(self, app):
"""asgi app """
""" 다음 단계에 처리할 앱을 가리킴 """
self.app = app
async def __call__(self, scope, receive, send):
# 오래 연결된것 삭제
# 너무 오래되면 서버에 문제가 생김
close_old_connections()
try:
# 쿼리스트링에서 'token' 파라미터 추출
token = parse_qs(scope["query_string"].decode("utf8")).get('token', None)[0]
# 토큰을 디코드해서 안에 들어있는 user_id 추출
data = jwt_decode(token, settings.SECRET_KEY, algorithms=["HS256"])
# scope['user']에 저장
scope['user'] = await self.get_user(data['user_id'])
except (TypeError, KeyError, InvalidSignatureError, ExpiredSignatureError, DecodeError):
scope['user'] = AnonymousUser()
return await self.app(scope, receive, send)
""" @database_sync_to_async는 ORM(DB 접근)을 비동기로 변환 """
@database_sync_to_async
def get_user(self, user_id):
# user_id로 DB에서 해당 유저를 조회
try:
return User.objects.get(id=user_id)
except User.DoesNotExist:
return AnonymousUser()
def JWTAuthMiddlewareStack(app):
return JWTAuthMiddleware(AuthMiddlewareStack(app))
import json
from channels.generic.websocket import AsyncWebsocketConsumer
"""chat/consumers.py"""
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
user = self.scope['user']
self.room_name = self.scope["url_route"]["kwargs"]["room_name"]
self.room_group_name = f"chat_{self.room_name}"
# Join room group
await self.channel_layer.group_add(self.room_group_name, self.channel_name)
if user.is_authenticated:
await self.accept()
else:
await self.close()
async def disconnect(self, close_code):
# Leave room group
await self.channel_layer.group_discard(self.room_group_name, self.channel_name)
# Receive message from WebSocket
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json["message"]
# Send message to room group
await self.channel_layer.group_send(
self.room_group_name, {"type": "chat.message", "message": message}
)
# Receive message from room group
async def chat_message(self, event):
message = event["message"]
# Send message to WebSocket
await self.send(text_data=json.dumps({"message": message}))
# asgi.py
import os
# from channels.auth import AuthMiddlewareStack
from chat.middleware import JWTAuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.security.websocket import AllowedHostsOriginValidator
from django.core.asgi import get_asgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "website.settings")
django_asgi_app = get_asgi_application()
from chat.routing import websocket_urlpatterns
application = ProtocolTypeRouter({
'http': django_asgi_app,
'websocket': JWTAuthMiddlewareStack(
URLRouter(
websocket_urlpatterns
)
)
})
from django.shortcuts import render
from rest_framework_simplejwt.tokens import AccessToken
# chat/views.py
def index(request):
token = AccessToken.for_user(request.user)
print("토큰값입니다: ", token)
return render(request, "chat/index.html", {"token": str(token)})