Wagtail은 Django 기반의 CMS다. 이 말인 즉슨 이미 풍부한 Django기능들을 그대로 쓸 수 있다는 뜻이다. 

이번에는 Django에서 중복로그인을 방지하는 Session처리에 대해서 기술해보려고 한다. 물론 Wagtail에서도 그대로 적용된다.

https://planjang.tistory.com/167

위의 블로그를 참고해서 작성했다. 생소한 내용이니 세세하게 공부해서 작성해볼 생각이다. 

 

먼저 쿠키와 세션에 대해서 알아보자

 

https://hahahoho5915.tistory.com/32

쿠키 : HTTP의 일종으로 사용자가 어떠한 웹 사이트를 방문할 경우,
그 사이트가 사용하고 있는 서버에서 사용자의 컴퓨터에 저장하는 작은 기록 정보 파일이다.

HTTP에서 클라이언트의 상태 정보를 클라이언트의 PC에 저장하였다가
필요시 정보를 참조하거나 재사용할 수 있다.

 

세션 : 일정 시간동안 같은 사용자(브라우저)로부터 들어오는
일련의 요구를 하나의 상태로 보고, 그 상태를 일정하게 유지시키는 기술이다.

여기서 일정 시간은 방문자가 웹 브라우저를 통해
웹 서버에 접속한 시점으로부터 웹 브라우저를 종료하여 연결을 끝내는 시점을 말한다.

 

즉, 로그인을 했을 때, 사용자 세션을 발생시키게 된다. 그래서 우리는 이 세션을 가지고 로그아웃을 관리할 수 있다. 다른 사이트에서 로그인이 되었을 때, 기존의 세션을 종료시키면 된다. 그렇다면 로그인이 되었다는 것을 어떻게 알 수 있을까? 그것은 signal을 사용하면 된다. 

 

Signal이란?

django 프레임워크는 어떤 특정한 일을 수행할 때마다 알려줄 것을 설정하고, 그 때에 지정한 동작을 수행할 수 있게 하는 신호(signal)를 발생하는 기능을 가지고 있다.
출처: https://dgkim5360.tistory.com/entry/django-signal-example [개발새발로그]

 

쉽게 얘기하자면 login을 할 때 signal을 발생시키게 되고 우리는 이를 이용하여 로그인을 한 것을 알 수 있다는 것이다. 

그렇다면 코드를 작성해보면서 지금의 내용을 구현해보자.

 

1. 현재 사용자의 세션을 저장하는 모델 객체 생성

user/models.py

from django.contrib.auth.models import AbstractUser
from django.db import models
from django.contrib.auth.signals import user_logged_in
from importlib import import_module
from django.conf import settings


# User 커스텀
class User(AbstractUser):
    nickname = models.CharField(max_length=20, null=True, default='None')


class UserSession(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, editable=False)
    session_key = models.CharField(max_length=40, editable=False)
    created_at = models.DateTimeField(auto_now_add=True)

나는 user app에서 User을 커스텀해서 사용중이었다. 먼저 사용자의 session을 관리하는 model을 만들어 준다. 각자 자신의 User을 가져와서 적용하면될 것이다. 

 

2. 세션 관리 리시버 구현

SessionStore = import_module(settings.SESSION_ENGINE).SessionStore


def kicked_my_other_sessions(sender, request, user, **kwargs):
    for user_session in UserSession.objects.filter(user=user):
        session_key = user_session.session_key
        session = SessionStore(session_key)
        session.delete()

    session_key = request.session.session_key
    UserSession.objects.create(user=user, session_key=session_key)

user_logged_in.connect(kicked_my_other_sessions, dispatch_uid='user_logged_in')
  • user_logged_in 시그널에 기존에 로그인한 세션을 삭제해주는 리시버를 연결해주자.
  • 뷰가 아닌 곳에서 세션을 사용하려면 공식문서(링크)에 나와있는대로 SessionStore를 사용하면 된다.
  • kicked_my_other_sessions 리시버를 만들었는데, 인자로 쓰이는 것들에 대해 알아보자.
    1. sender: 방금 로그인을 마친 유저의 클래스
    2. request: 현재 HttpRequest 인스턴스
    3. user: 방금 로그인을 마친 유저 인스턴스
  • 따라서 함수에서 구현한 사항은
    1. 방금 로그인을 마친 유저가 가지고 있는 UserSession 클래스의 레코드를 모두 읽어온다.
    2. 세션을 삭제하여 로그아웃한다.
    3. 현재 세션에 대한 레코드를 생성하여 저장한다.

위에서 소개한 블로그에 자세하게 나와 있어서 인용해왔다. 

 

분석하자면 SessionStore을 가져와서 user에 맞는 session을 찾아서 삭제한다. (로그아웃)

그리고 현재 세션을 Session에 다시 저장해주는 방법이다. 이렇게 되면 현재 요청들어온 세션만 유지되고 나머지 세션은 전부 삭제되어서 다른 곳에서 로그아웃이 가능해진다. 

 

추가) 로그아웃이 될 때 메세지 출력

로그아웃이 될 때, 로그아웃 메세지를 표현해주는 방법에 대해서 기술하려고 한다. Wagtail에 적용했는데 Wagtail admin login 페이지에만 표현 되어 이후 수정이 필요해보인다. 일단은 블로그 내용을 정리하는데 중점을 맞췄으니 이 내용도 정리하려고 한다.

 

settings에 다음 코드를 추가한다. django는 settings.py이고 wagtail은 settings/base.py이다.

MIDDLEWARE = [
    # ...
    'django.contrib.messages.middleware.MessageMiddleware',
    'user.middleware.KickedMiddleware',
    # ...
]

'user.middleware.KickedMiddleware'은 자신의 middleware(메세지발생) 코드가 위치하는 곳이다.

 

user/middleware

from django.conf import settings
from django.contrib import messages
from django.contrib.auth import logout as auth_logout
from django.shortcuts import redirect
from django.utils.deprecation import MiddlewareMixin

class KickedMiddleware(MiddlewareMixin):
    def process_request(self, request):
        kicked = request.session.pop('kicked', None)
        if kicked:
            messages.info(request, '동일 아이디로 다른 브라우저 웹사이트에서 로그인이 감지되어, 강제 로그아웃되었습니다.')
            auth_logout(request)
            return redirect(settings.LOGIN_URL)
  • settings.py에 등록한 이름으로 클래스를 생성한다.
  • 현재 세션에 'kicked'가 True면 메시지를 띄우고 로그아웃을한다.

 

코드는 간단하다. 세션에 kicked라는 이벤트가 발생했을 때, 메세지를 호출하고 로그아웃 하고 redirect하라는 것이다. models.py에 kicked를 발생시켜주면 된다. 

 

user/models.py

def kicked_my_other_sessions(sender, request, user, **kwargs):
    for user_session in UserSession.objects.filter(user=user):
        session_key = user_session.session_key
        session = SessionStore(session_key)
        session['kicked'] = True
        session.save()
        user_session.delete()

    session_key = request.session.session_key
    UserSession.objects.create(user=user, session_key=session_key)

user_logged_in.connect(kicked_my_other_sessions, dispatch_uid='user_logged_in')
  • 미들웨어 추가에 따른 코드 변경이 필요하다.
  • 기존에 로그인했던 세션(user_session)에 kicked = True를 할당한다.
  • 원래 session middleware에서 save() 처리를 하는데, 지금은 middleware를 쓰지 않으니 직접 save를 호출 해야한다.

위와 같이 수정해주면 된다. 

'Back-End > Wagtail, Django' 카테고리의 다른 글

Wagtail StyleGuide + 추가 모듈  (0) 2021.10.18
Wagtail Iamport 결제, 결제 페이지  (0) 2021.10.14
Wagtail Video  (0) 2021.10.01
Wagtail Custom Widget (+Model)  (0) 2021.09.29
Wagtail Custom Field, Panel  (0) 2021.09.29

+ Recent posts