간단히 email과 password를 받아 로컬 로그인을 구현해보도록하자.
login, authentication 로직은 우선 생략하고 간단히 로그인과 비밀번호를 받는 form을 구현해보자.
from django import forms
from . import models
class LoginForm(forms.Form):
# passwordField는 없습니다
email = forms.EmailField()
password = forms.CharField(widget=forms.PasswordInput)
# 필드 간의 관계 등 다른 validation을 위해서는 clean함수를 사용하자.
# clean_[필드명] 꼴이 verbose하다고 생각하면 clena으로 한 방에 통합해도 된다.
def clean(self):
email = self.cleaned_data.get("email")
password = self.cleaned_data.get("password")
try:
user = models.User.objects.get(email=email)
if user.check_password(password):
return self.cleaned_data
else:
# clean 메서드를 사용할 시 어느 필드에서 오류가 났는지 add_error에서 명시해줄 것
self.add_error("password", forms.ValidationError("password is wrong"))
except models.User.DoesNotExist:
self.add_error("email", forms.ValidationError("User does not exist"))
from django.shortcuts import render
from django.views import View
from . import forms
# Create your views here.
class LoginView(View):
def get(self, request):
form = forms.LoginForm()
return render(request, "users/login.html", {"form": form})
def post(self, request):
pass
<!-- base -->
{% extends "base.html" %}
<!-- pagename block -->
{% block page_name%} Login {% endblock page_name%}
<!-- block-->
{% block content %}
<div>Login</div>
<form method="POST" action="{% url "users:login" %}">
{% csrf_token %}
{{form.as_ul}}
<button>Login</button>
</form>
{% endblock content %}
{% csrf_token %} ?
{% csrf_token %}을 제외하고 form을 작성하고 submit을 날려보면 403 forbidden이 뜬다.
CSRF란 Cross Site Request Forgery의 약자이다.
해당 공격과 방어 메카니즘은 공식 문서를 참고하자
docs.djangoproject.com/en/2.2/ref/csrf/
공식문서와 별도로 CSRF를 프론트 javasciprt 단에서 해결하는 방법도 아래 포스트에서 소개하고 있으니 참고해보자.
결국 해당 웹사이트에서 보낸 요청인지를 식별하는 되는 문제이다. django에서는 csrf_token 태그를 form 태그 내부에 넣어서 간단히 해결할 수 있다.
In any template that uses a POST form, use the csrf_token tag inside the <form> element if the form is for an internal URL, e.g.:
<form method="post">{% csrf_token %}
This should not be done for POST forms that target external URLs, since that would cause the CSRF token to be leaked, leading to a vulnerability.
즉, 아래와 같이 {% csrf_token %} 만 추가해주면 되겠다.
<form method="POST" action="{% url "users:login" %}">
{% csrf_token %}
{{form.as_ul}}
<button>Login</button>
</form>
추가한 다음 관리자 창을 켜보면 다음과 같이 토큰을 확인할 수 있다.
authenticate/login
[공식문서](docs.djangoproject.com/en/3.1/topics/auth/default/#how-to-log-a-user-in)
django.contrib.auth에서 제공하는 authenticate 메서드와 login, logout 메서드를 통해 쉽게 로그인시킬 수 있다.
우선 clean() 혹은 clean_[메서드]()를 통해 form Validation까지 끝냈다면 통과한 cleaned_data을 이용하여 로그인을 시켜봅시다.
from django.shortcuts import render, redirect, reverse
from django.views import View
from django.contrib.auth import authenticate, login
from . import forms
# Create your views here.
class LoginView(View):
def get(self, request):
form = forms.LoginForm()
return render(request, "users/login.html", {"form": form})
def post(self, request):
form = forms.LoginForm(request.POST)
# is_valid() : clean()을 전부 통과하면 True 반환
if form.is_valid():
# clean()에서 return한 data를 확인할 수 있습니다.
email = form.cleaned_data.get("email")
password = form.cleaned_data.get("password")
user = authenticate(request, username=email, password=password)
if user is not None:
login(request, user)
return redirect(reverse("core:home"))
return render(request, "users/login.html", {"form": form})
authentication check
해당 유저가 로그인 중인지, 로그인 중이 아닌지를 체크하기 위해 is_authenticated 속성을 체크합니다.
다음과 같이 사용할 수 있습니다.
<ul>
{% if user.is_authenticated %}
<li><a href="{% url "users:login" %}">Log out</a></li>
{% else %}
<li><a href="{% url "users:login" %}">Log in</a></li>
{% endif %}
</ul>
logout
django.contrib.auth 에서 logout 함수를 이용하면 됩니다.
from django.contrib.auth import logout
def logout_view(request):
logout(request)
# Redirect to a success page.
다른 방법들
(1) LoginView를 이용하기
username을 아이디로 사용하도록 강제하기 때문에, 또 추상화의 정도가 높기 때문에 여기서는 사용하지 않기로 했다.
docs.djangoproject.com/en/3.1/topics/auth/default/#django.contrib.auth.views.LoginView
(2) FormView를 이용해보기
위와 같은 방법으로 login/logout을 구현할 수 있지만 조금 더 편리하고 코드를 덜 작성하고 싶다면 FormView를 이용해보는 것도 방법이다.
절충안으로 FormView를 사용하곤 한다.
https://docs.djangoproject.com/en/3.1/topics/auth/default/#all-authentication-views
ccbv.co.uk/projects/Django/3.0/django.views.generic.edit/FormView/
from django.shortcuts import render, redirect, reverse
from django.urls import reverse_lazy
from django.views.generic import FormView
from django.contrib.auth import authenticate, login, logout
from . import forms
class LoginView(FormView):
template_name = "users/login.html"
form_class = forms.LoginForm
success_url = reverse_lazy("core:home") # reverse를 쓰면 Error
def form_valid(self, form):
"""If the form is valid, redirect to the supplied URL."""
email = form.cleaned_data.get("email")
password = form.cleaned_data.get("password")
user = authenticate(self.request, username=email, password=password)
if user is not None:
login(self.request, user)
return super().form_valid(form)
'Django, Flask > 🔫 Django' 카테고리의 다른 글
django + PostgreSQL (0) | 2020.08.13 |
---|---|
Forms API 와 validation (0) | 2020.07.03 |
404 에러 핸들링 (0) | 2020.06.18 |
get_absolute_url로 admin panel에서 곧바로 view로 가자 (0) | 2020.06.16 |
Path Converter와 namespace, name을 활용한 link (0) | 2020.06.16 |