Django : Authentication 1

김재훈·2024년 3월 31일

Django

목록 보기
9/15

HTTP

  • HTML 문서와 같은 리소스들을 가져올 수 있도록 해주는 규약

    웹(WWW)에서 이루어지는 모든 데이터 교환의 기초

HTTP 특징

  • 비 연결 지향(connectionless)
    • 서버는 요청에 대한 응답을 보낸 후 연결을 끊음
  • 무상태(stateless)
    • 연결을 끊는 순간 클라이언트와 서버 간의 통신이 끝나며 상태 정보가 유지되지 않음

쿠키(Cookie)

  • 서버가 사용자의 웹 브라우저에 전송하는 작은 데이터 조각

    클라이언트 측에서 저장되는 작은 데이터 파일이며, 사용자 인증, 추적, 상태 유지 등에 사용되는 데이터 저장 방식

  • 서버로부터 쿠키를 받고 같은 서버에 다른 페이지로 재 요청시마다 저장해 놓았던 쿠키를 함께 전송

쿠키 사용 원리

  1. 브라우저(클라이언트)는 쿠키를 KEY-VALUE의 데이터 형식으로 저장
  2. 이렇게 쿠키를 저장해 놓았다가, 동일한 서버에 재요창 시 저장된 쿠키를 함께 전송

    쿠키는 두 요청이 동일한 브라우저에서 들어왔는지 아닌지를 판단할 때 주로 사용됨

  • 이를 통해 사용자의 로그인 상태를 유지할 수 있음
  • 상태가 없는(stateless) HTTP 프로토콜에서 상태 정보를 기억시켜 주기 때문

쿠키 사용 목적

  1. 세션 관리(Session management)
    • 로그인, 아이디 자동완성, 공지 하루 안보기, 팝업 체크, 장바구니 등의 정보 관리
  2. 개인화(Personalization)
    • 사용자 선호, 테마 등의 설정
  3. 트래킹(Tracking)
    • 사용자 행동을 기록 및 분석

쿠키 종류별 Lifetime (수명)

  1. Session cookie
    • 현재 세션(current session)이 종료되면 삭제됨
    • 브라우저 종료와 함께 세션이 삭제됨
  2. Persistent cookies
    • Expires 속성에 지정된 날짜 혹은 Max-Age 속성에 지정된 기간이 지나면 삭제됨

세션(Session)

  • 서버 측에서 생성되어 클라이언트와 서버 간의 상태를 유지
  • 상태 정보를 저장하는 데이터 저장 방식

    쿠키에 세션 데이터를 저장하여 매 요청시마다 세션 데이터를 함께 보냄

세선 작동 원리

  1. 클라이언트가 로그인을 하면 서버가 session 데이터를 생성 후 저장
  2. 생성된 session 데이터에 인증할 수 있는 session id를 발급
  3. 발급한 session id를 클라이언트에게 응답
  4. 클라이언트는 응답 받은 session id를 쿠키에 저장
  5. 클라이언트가 다시 동일한 서버에 접속하면 요청과 함께 쿠키(session id가 저장된)를 서버에 전달
  6. 쿠키는 요청 때마다 서버에 함께 전송되므로 서버에서 session id를 확인해 로그인되어 있다는 것을 알도록 함
  • 서버 측에서는 세션 데이터를 생성 후 저장하고 이 데이터에 접근할 수 있는 세션 ID를 생성
  • 이 ID를 클라이언트 측으로 전달하고, 클라이언트는 쿠키에 이 ID를 저장
  • 이후 클라이언트가 같은 서버에 재요청 시마다 저장해 두었던 쿠키도 요청과 함께 전송

    예를 들어 로그인 상태 유지를 위해 로그인 되어있다는 사실을 입증하는 데이터를 매 요청마다 계속해서 보내는 것

세션 in Django

  • Django는 'database-backed sessions' 저장 방식을 기본 값으로 사용
  • session 정보는 DB의 django_session 테이블에 저장됨
  • Django는 요청안에 특정 session id를 포함하는 쿠키를 사용해서 각각의 브라우저와 사이트가 연결된 session 데이터를 알아냄

    Django는 우리가 session 매커니즘(복잡한 동작원리)에 대부분을 생각하지 않게끔 많은 도움을 줌


Authentication System

  • 인증 시스템
  • 사용자 인증과 관련된 기능을 모아 놓은 시스템

인증(Authentication)

  • 사용자가 자신이 누구인지 확인하는 것(신원 확인)

  • 사전 준비

    • 두 번째 app accounts 생성 및 등록
    • auth와 관련한 경로나 키워드들을 django 내부적으로 accounts라는 이름으로 사용하고 있기 때문에 되도록 'accounts'로 지정하는 것을 권장

Custom User Model로 대체하기


  1. AbstractUser 클래스를 상속받는 커스텀 User 클래스 작성

    기존 User 클래스도 AbstractUser를 상속받기 때문에, 이렇게 하면 커스텀 User 클래스도 기존 User 클래스와 완전히 같은 모습을 가지게 됨.

# accounts/models.py

from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
	pass
  1. django 프로젝트가 사용하는 기본 User 모델을 우리가 작성한 User 모델로 지정
    • 수정 전 기본 값은 'auth.User'
    • 프로젝트 중간에 AUTH_USER_MODLE을 변경할 수 없음
# settings.py

AUTH_USER_MODEL = 'accounts.User'
  1. admin site에 대체한 User 모델 등록
    • 기본 User 모델이 아니기 때문에 등록하지 않으면 출력되지 않기 때문
# accounts/admin.py

from django.contrib import admin
from django.contrib.auth.admin import UserAdming
from .models import User

admin.site.register(User, UserAdmin)

AUTH_USER_MODEL

  • Django 프로젝트의 User를 나타내는 데 사용하는 모델을 지정

  • 프로젝트 중간에 AUTH_USER_MODEL을 변경할 수 없음

    • 이미 프로젝트가 진행되고 있을 경우 데이터베이스 초기화 후 진행
  • Django는 새 프로젝트를 시작하는 경우 (기본 User 모델이 충분하더라도) 커스텀 User 모델을 설정하는 것을 강력하게 권장하고 있음

  • 커스텀 User 모델은 기본 User 모델과 동일하게 작동 하면서도 필요한 경우 나중에 맞춤 설정할 수 있기 때문

  • 단, User 모델 대체 작업은 프로젝트의 모든 migrations 혹은 첫 migrate를 실행하기 전에 이 작업을 마쳐야 함

데이터베이스 초기화

  1. DB 삭제
  2. migrations 파일 전부 삭제 (폴더는 삭제 금지)

Login

  • Session을 Create하는 과정

AuthenticationForm()

  • 로그인 인증에 사용할 데이터를 입력 받는 built-in form

accounts/urls.py

app_name = 'accounts'
urlpatterns = [
	path('login/', views.login, name='login'),
]

accounts/login.html

<h1>로그인<h1>
<form action="{% url 'accounts:login' %}" method="POST">
  {% csrf_token %}
  {{ form.as_p }}
  <input type="submit">
</form>

accounts/views.py

from django.contrib.auth.forms import AuthenticationForm
def login(request):
	if request.method == 'POST':
    	form = AuthenticationForm(request, data=request.POST)
        if form.is_valid():
        	auth_login(request, form.get_user())
            return redirect('articles:index')
    else:
    	form = AuthenticationForm()
    context = {
    	'form': form,
    }
    return render(request, 'accounts/login.html', context)

login(request, user)

  • AuthenticationForm을 통해 인증된 사용자를 로그인 하는 함수
  • 로그인 후 세션 데이터 확인
    • 개발자 도구 - Application - Cookies
    • DB - django_session

get_user()

  • AuthenticationForm의 인스턴스 메서드

    유효성 검사를 통과했을 경우 로그인 한 사용자 객체를 반환


Logout

  • Session을 Delete하는 과정

logout(request)

  • 현재 요청에 대한 Session Data를 DB에서 삭제
  • 클라이언트의 쿠키에서도 Session Id를 삭제

accounts/urls.py

urlpatterns = [
	path('login/', views.login, name='login'),
    path('logout/', views.logout, name='logout'),
]

articles/index.html

<h1>Articles</h1>
<a href="{% url 'accounts:login' %}">Login</a>
<form action="{% url 'accounts:logout' %} method="POST">
  {% csrf_token %}
  <input type="submit" value="Logout">
</form>

accounts/views.py

from django.contrib.auth import logout as auth_logout
def logout(request):
	auth_logout(request)
    return redirect('articles:index')

Template with Authentication data

  • 템플릿에서 인증 관련 데이터를 출력하는 방법
  • django가 미리 준비한 context 데이터가 존재 (context processors)

    settings.py

TEMPLATE = [
	{
    	...,
        "OPTIONS": {
        	"context_processors": [
            	"django.template.context_processors.debug",
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.contrib.messages.context_processors.messages",
            ],
        },
    },
]

회원 가입

  • User 객체를 Create 하는 과정

UserCreationForm()

  • 회원 가입시 사용자 입력 데이터를 받는 built-in ModelForm
def signup(request):
	if request.method =="POST":
    form = UserCreationForm(request.POST)
    if form.is_valid():
    	form.save()
        return redirect('articles:index')
    else:
    	form = UserCreationForm()
    Context = {
    	'form': form,
    }
    return render(request, 'accounts/signup.html', context)

회원가입 로직 에러

get_user_model()

  • 현재 프로젝트에서 활성화된 사용자 모델(active user model)을 반환하는 함수

User 모델을 직접 참조하지 않는 이유

  • get_user_model()을 사용해 User 모델을 참조하면 커스텀 User 모델을 자동으로 반환해주기 때문
  • Django는 필수적으로 User 클래스를 직접 참조하는 대신 get_user_model()을 사용해 참조해야 한다고 강조하고 있음

    User model 참조에 대한 자세한 내용은 추후 모델 관계에서 다룰 예정

# accounts/views.py

from .forms import CustomUserCreationForm

def signup(request):
	if request.method == "POST":
    	form = CustomUserCreationForm(request.POST)
        if form.is_valid():
        	form.save()
            return redirect('articles:index')
    else:
    	form = CustomUserCreationForm()
    context = {
    	'form': form,
    }
    return render(request, 'accounts/signup.html', context)

회원 탈퇴

  • User 객체를 Delete하는 과정

회원 정보 수정

  • User 객체를 Update하는 과정

UserChangeForm()

  • 회원정보 수정 시 사용자 입력 데이터를 받는 built-in ModelForm
  • UserChangeForm 사용시 문제점
    • User 모델의 모든 정보들(fields)까지 모두 출력되어 수정이 가능하기 때문에 일반 사용자들이 접근해서는 안되는 정보는 출력하지 않도록 해야 함

CustomUserChangeForm에서 접근 가능한 필드를 다시 조정

PasswordChangeForm()

  • 비밀번호 변경시 사용자 입력 데이터를 받는 built-in form

세션 무효화 방지

  • 비밀번호가 변경되면 기존 세션과의 회원 인증 정보가 일치하지 않게 되어버려 로그인 상태가 유지되지 못하고 로그아웃 처리됨
  • 비밀번호가 변경되면서 기존 세션과의 회원 인증 정보가 일치하지 않기 때문

update_session_auth_hash(request, user)

  • 암호 변경시 세션 무효화를 막아주는 함수

    암호가 변경되면 새로운 password의 Session Date로 기존 session을 자동으로 갱신

인증된 사용자에 대한 접근 제한

  1. is_authenticated 속성 (attribute)
    • 사용자가 인증되었는지 여부를 알 수 있는 User model의 속성

      모든 User 인스턴스에 대해 항상 True인 읽기 전용 속성이며, 비인증 사용자에 대해서는 항상 False

    • 인증된 사용자라면 로그인/회원가입 로직을 수행할 수 없도록 하기
  2. login_required 데코레이터 (decorator)
    • 인증된 사용자에 대해서만 view 함수를 실행시키는 데코레이터

      비인증 사용자의 경우 /accounts/login/ 주소로 redirect 시킴

    • 인증된 사용자만 게시글을 작성/수정/삭제할 수 있도록 수정

0개의 댓글