Django - 로그인

Lee Ju-Hyeon(David)·2021년 9월 15일
1

Django

목록 보기
9/10
post-thumbnail

1. Django의 세션

  • 미들웨어로 구현됨
  • 기본적으로 세션을 데이터베이스(django_session 테이블)에 저장하고, 설정을 통해 바꿀 수 있다.
  • SessionMiddleware : 요청 전반에 걸쳐 세션 관리
  • AuthenticationMiddleware : 세션을 이용해 사용자를 요청과 연결

2. 로그인 구현

Django는 세션을 DB에 저장하는데, DB에 데이터를 저장하려면 Model이나 Form을 정의해야 한다. 이런 번거로움을 해결하기 위해 Django는 built-in-function 형태로 로그인 From을 제공한다.

2.1 AuthenticationForm

  • 유저가 로그인할 수 있도록 도와주는 Form
  • request를 첫 위치 인자로 받는다.

2.2 로그인 추가하기

먼저 로그인을 구현하기 위한 앱을 추가한다.

$ python manage.py startapp accounts

앱을 생성한 뒤, 이번에도 마찬가지로

  1. url 설정
  2. view 함수 정의
  3. template 생성

의 순서로 진행하면 된다. url 설정은 생략.

from django.contrib.auth.forms import AuthenticationForm

로그인을 구현해 놓은 Form을 사용하기 위해 AuthenticationForm을 import 해준다.

from django.shortcuts import redirect, render
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth import login as auth_login

def login(request):
    if request.method == 'POST':
        #form은 로그인을 실행시켜주진 않는다 데이터만 받아올 뿐
        form = AuthenticationForm(request, request.POST)
        if form.is_valid():
            auth_login(request, form.get_user())
            return redirect('records:index')
    else:
        form = AuthenticationForm()
    context = {
        'form': form
    }
    return render(request, 'accounts/login.html', context)

login.html페이지를 접속하면 로그인을 위한 페이지가 랜더링 된다.

Form을 따로 정의해주지 않았음에도 불구하고 로그인 Form이 만들어진 것을 확인할 수 있는데, 이것이 Django에서 기본적으로 제공해주는 로그인 Form이다.

사용자로부터 POST 요청으로 로그인 데이터를 입력받으면, 이 데이터를 AuthenticationForm을 이용해 저장한다. 이 데이터를 가지고 로그인을 진행하는데 AuthenticationForm 자체에서 로그인을 진행하진 않고, login이라는 함수를 통해서 진행한다.

login함수를 보면 두 번째 인자로 user를 입력 받는다. 이를 AuthenticationForm인스턴스의 get_user()메서드를 이용한다.

get_user()

AuthenticationForm 의 인스턴스 메서드로 기본적으로 None값이 할당되며 유효성 검사를 통과하면(로그인이 되면) 해당 사용자 객체로 할당된다.

이후 이전에 만들었던 관리자 계정으로 로그인을 하면 index페이지로 redirect된다.개발자 페이지를 확인하면 로그인에 대한 Session과 쿠키를 저장한 것을 볼 수 있다.Django의 특징대로 데이터베이스에 세션을 저장했다. 브라우저에는 session_key만 저장하고 session_data는 데이터베이스에 저장한다.


2.3 로그인 여부 표시하기

base.html에 사진과 같이 유저의 정보를 표기해줬다.화면에 잘 표기되는 것을 확인할 수 있다.(ID를 1로 설정해서 1이라고 나온다.) 로그인 세션을 삭제하게 되면 AnonymousUser라고 표기된다.

위의 사진에 OPTIONS에 해당하는 것은 모든 template에서 사용할 수 있도록 정의해둔 것이다. 따라서 base.html에서 context를 전달 받지 않고도 user라는 context를 사용할 수 있다.

2.4 로그아웃 추가하기

로그아웃은 Delete와 비슷한 로직이다. Django는 세션에 대한 데이터를 데이터베이스에 저장한다. 이 데이터를 삭제하는 것이 로그아웃이다.
logout함수는 데이터베이스의 세션 데이터 뿐만 아니라 클라이언트의 쿠키까지 삭제한다. 이는 이전 사용자의 세션 데이터에 접근하는 것을 방지하기 위함이다.

@require_POST
def logout(request):
    auth_logout(request)
    return redirect('records:index')

url 설정을 하고 view에 logout함수를 정의했다. 로그아웃을 진행하면
Session이 삭제되고 유저 이름도 바뀐 것을 확인할 수 있다.

2.5 로그인 여부에 따른 유저 접근 제한

지금 만들어진 페이지를 보면 로그인 여부에 상관 없이 로그인과 로그아웃이 가능하고, CRUD 동작도 가능하다.

로그인을 했을 때만, 혹은 로그아웃 했을 때만 엑세스가 가능한 기능들은 그에 맞게 접근을 제한할 필요가 있다.

로그인 사용자에 대한 엑세스를 제한하는 방법은

  • is_authenticated 속성을 이용한 방법
  • login_required 데코레이터를 이용한 방법

두 가지가 있다.

먼저, is_authenticated은 User model에 속성 중 하나로, 모든 유저에 대해서는 항상 True, AnonymousUser에 대해서는 False를 나타낸다. 권한이랑 관련이 없으며 사용자가 활성화 상태인지 유효성한 세션인지 검사하지 않는다.base.html에서 is_authenticated 속성을 이용해서 if-else분기 처리를 했고, 로그인 상태일 때와 로그아웃 상태일 때를 나누었다.

하지만 아직 버튼을 이용한 요청 외에도 url을 이용해서도 로그인 페이지를 요청할 수 있다. 즉, 로그인 상태일 때도 로그인 페이지를 요청할 수 있다.위의 두 줄의 코드를 추가함으로써 로그인 상태라면 어떠한 방법으로도 로그인 페이지를 들어갈 수 없게 되었다.

CRUD 기능도 로그인 여부에 따라 접근을 제한할 것인데, login_required를 이용할 것이다.

login_required은 로그인되어 있지 않으면 settiings.LOGIN_URL에 설정된 문자열 기반 경로로 redirect한다. settiings.LOGIN_URL의 기본 값은 '/accounts/login/'이다.

앞서 사용해본 is_authenticated과는 달리 인증에 성공하면 사용자가 redirect 되어야하는 경로를 next라는 쿼리 문자열 매개 변수에 저장한다.

login_required지정하면 로그인되어 있지 않은 상태에서는 다음과 같이 된다.url을 이용해서 create 페이지를 요청하면 다음과 같이 로그인 페이지로 redirect 된다. 여기서 주소를 살펴보면

http://127.0.0.1:8000/accounts/login/?next=/records/create/

next 라는 key값에 create에 해당하는 주소를 저장해 놓은 것을 볼 수 있다. 이는 login_required가 로그인 페이지로 redirect함과 동시에 원래 요청했던 create로 redirect할 수 있는 주소를 추가한 것이다. 로그인을 하면 바로 create로 redirect해준다.

하지만 next값이 있다고 해서 자동으로 create로 redirect해주는 것은 아니다.위처럼 next값을 받아서 사용해야 한다.

next는 url에 저장된 정보이기 때문에 문제가 발생한다. Create나 Update는 GET요청이 들어왔을 때, 해당 동작을 위한 데이터를 기입하는 페이지를 랜더링해주지만, Deleted의 경우 POST 요청만 허용되기 때문에 문제가 발생한다.

@login_required
# @require_POST
def delete(request, pk):
    if request.user.is_authenticated:
        record = Record.objects.get(pk=pk)
        record.delete()
    return redirect('records:index')

위와 같이 POST요청방식만 허용하는 데코레이터를 제거하면 next파라미터를 이용한 GET요청에도 삭제 처리를 해주겠지만, 이는 보안상 좋지 않으므로 지양해야 한다.

@login_required
# @require_POST
def delete(request, pk):
    # if request.user.is_authenticated:
    if request.method == 'POST':
        record = Record.objects.get(pk=pk)
        record.delete()
    return redirect('records:index')

따라서 위와 같이 로그인을 요구하면서 POST 요청일 때만 삭제가 진행되도록 구성했다.

0개의 댓글