jc.jang

11강 이메일을 통한 URL 로그인 만들기 본문

Django/Django - 인증편

11강 이메일을 통한 URL 로그인 만들기

jangstory 2019. 9. 3. 12:27

주제

  • 비밀번호를 기억하지 않고 이메일만으로 로그인을 구현할 수 있다.
  • 로그인하기 위해서는 아이디와 비밀번호를 기억해야한다. 하지만 자주 방문하는 서비스가 아니라면, 사용자의 이메일에 로그인 가능한 URL을 제공하면 더이상 비밀번호를 기억할 필요가 없다.

노트

from django.urls import path
from django.contrib.auth import views as auth_views
from . import views

urlpatterns = [
    # ...
    path('login/url/', views.RequestLoginViaUrlView.as_view(), name='request_login_via_url'),
    path('login/<uidb64>/<token>/', views.login_via_url, name='login_via_url'),
    # ...
]
  • 사용자가 URL 로그인을 요청하기 위해 'login/url/'을 방문함
  • 이 곳에서 사용자의 이메일을 입력하면 'URL 로그인'용 URL을 메일로 전송함
  • 'URL 로그인'용 URL은 'login/<uidb64>/<token>/' 형식으로 구성되어 있고 방문 시 views.py에서 실제 사용자인지 확인함

 

  • accounts/views.py
from django.core.exceptions import ValidationError
from django.contrib import messages
from django.contrib.auth.views import PasswordResetView
from django.contrib.auth.tokens import default_token_generator
from django.utils.http import urlsafe_base64_decode
from django.http import Http404


class RequestLoginViaUrlView(PasswordResetView):
    template_name = 'accounts/request_login_via_url_form.html'
    title = '이메일을 통한 로그인'
    email_template_name = 'accounts/login_via_url.html'
    success_url = settings.LOGIN_URL
    
    
def login_via_url(request, uidb64, token):
    User = get_user_model()
    try:
        uid = urlsafe_base64_decode(uidb64).decode()
        current_user = User.objects.get(pk=uid)
    except (TypeError, ValueError, OverflowError, User.DoesNotExist, ValidationError):
        raise Http404

    if default_token_generator.check_token(current_user, token):
        auth_login(request, current_user)
        messages.info(request, '로그인이 승인되었습니다.')
        return redirect('root')
    
    messages.error(request, '로그인이 거부되었습니다.')
    return redirect('root')
  • 사용자가 'login/url/' 방문 시 이메일을 입력하면 입력받은 이메일로 'accounts/login_via_url.html' 템플릿에 맞춰 이메일을 전송함. 이 이메일에는 로그인용 URL이 담겨있음
  • 사용자가 이메일을 확인하여 '{{ protocol }}://{{ domain }}{% url "login_via_url" uidb64=uid token=token %}' 형식의 URL을 클릭하면 'login_via_url' 함수에서 존재하는 User인지, token 값이 유효한지 확인하고 로그인 혹은 로그인 거부를 함

 

  • accounts/templates/accounts/request_login_via_url_form.html
{% extends "accounts/layout.html" %}

{% block content %}
    <h2>이메일로 로그인</h2>

    <form action="" method="POST">
        {% csrf_token %}
        <table>
            {{ form.as_table }}
        </table>
        <input type="submit"/>
    </form>
{% endblock %}
  • 이메일을 입력 받음

 

  • accounts/templates/accounts/login_via_url.html
아래 주소를 통해 {{ site_name }}에 로그인하실 수 있습니다.

링크: {{ protocol }}://{{ domain }}{% url "login_via_url" uidb64=uid token=token %}
  • 'URL 로그인'용 링크를 전송함

질문

  • 강의에서 사용한 방식은 이미 회원가입한 사용자에 대해서만 조회를 한다.
  • 아예 회원가입을 이메일만 받고 로그인할 때 마다 이메일을 확인하도록 하는 것도 가능할 것 같다(pycon에서 사용하는 방식)
  • pycon2018 views.py
@never_cache
def login_req(request, token):
    time_threshold = datetime.now() - timedelta(hours=1)

    try:
        token = EmailToken.objects.get(token=token,
                                       created__gte=time_threshold)
    except ObjectDoesNotExist:
        return render(request, 'login_notvalidtoken.html',
                      {'title': _('Not valid token')})
    email = token.email

    # Create user automatically by email as id, token as password
    try:
        user = User.objects.get(email=email)
    except ObjectDoesNotExist:
        user = User.objects.create_user(email, email, token)
        if is_pycon_user(email):
            user.is_staff = True
            user.is_superuser = True
        user.save()

    # Set backend manually
    user.backend = 'django.contrib.auth.backends.ModelBackend'
    user_login(request, user)

    return redirect(reverse('index'))
  • 토큰을 비밀번호로하여 User를 생성함

요약

  • PasswordResetView를 응용하여 URL 로그인을 구현할 수 있다.

날짜

  • 오후 12시, 20190903
Comments