jc.jang

17강 중복 로그인 막기 본문

Django/Django - 인증편

17강 중복 로그인 막기

jangstory 2019. 9. 9. 16:50

주제

  • session을 이용해 중복 로그인을 막는다.
  • 중복 로그인 시 middleware message로 사용자에게 알림 메시지를 노출한다.

노트

  • 한 사용자가 A 브라우저에서 로그인 -> B 브라우저에서 로그인한 경우, A 브라우저의 세션을 종료 시킨다.
  • django/contrib/auth/__init__(링크) 에서 보면 login 메소드는 로그인이 성공적으로 실행됐을 때, user_logged_in 시그널을 호출하는 것을 알 수 있다.
 

django/django

The Web framework for perfectionists with deadlines. - django/django

github.com

  •  그렇다면 로그인 성공 시 user_logged_in 시그널을 호출하고 커스텀 리시버를 설정하여 기존에 연결된 세션을 끊어서 다중 로그인을 방지하자.
  • 예전에 User 레코드 생성 시 Profile을 생성할 때 시그널을 사용했지만, 기억이 나지 않는다면 signals 공식문서(링크)를 참고하자.

 

  • accounts/models.py
from django.conf import settings
from django.db import models


class UserSession(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, editable=False)
    session_key = models.CharField(max_length=40, editable=False)
    created_at = models.DateTimeField(auto_now_add=True)
  • 현재 사용자의 세션을 저장하는 모델 객체 생성, (마이그레이션 필요)

 

  • account/models.py
from django.conf import settings
from django.contrib.auth.signals import user_logged_in
from django.db import models
from importlib import import_module


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. 현재 세션에 대한 레코드를 생성하여 저장한다.

 

  • 만약 중복 로그인을 통해 로그아웃된 경우, 사용자에게 메시지를 노출 시키고 싶다면?
  • 프로젝트/settings.py
MIDDLEWARE = [
    # ...
    'django.contrib.messages.middleware.MessageMiddleware',
    'accounts.middleware.KickedMiddleware',
    # ...
]
  • middleware를 추가한다.

 

  • accounts/middleware.py
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면 메시지를 띄우고 로그아웃을한다.

 

  • accounts/models.py
from django.conf import settings
from django.contrib.auth.signals import user_logged_in
from django.db import models
from importlib import import_module


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['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를 호출 해야한다.

 

 

질문

  • 쿠키와 세션에 대해 정리할 때가 온 것 같다...
  • middleware를 처음 써봤는데 뭔지 잘 모르겠다

요약

  • session을 확인하여 중복 로그인을 막을 수 있다.

날짜

  • 오후 5시, 20190909

 

Comments