일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- AWS
- 백준
- django ORM
- es6
- Algorithm
- c++
- js
- CSS
- 알고리즘 문제
- Git
- react
- DRF
- 장고
- 알고리즘 연습
- MAC
- 알고리즘 풀이
- Baekjoon
- HTML
- java
- 알고리즘
- 파이썬 알고리즘
- django rest framework
- javascript
- web
- 파이썬
- PYTHON
- form
- Django
- API
- django widget
- Today
- Total
수학과의 좌충우돌 프로그래밍
[Django] 회원가입 시 이메일 인증, SMTP 본문
SMTP
SMTP 는 Simple Mail Transfer Protocol 의 약자로 전자 메일 전송을 위한 표준 프로토콜입니다. 이를 이용해서 인증메일을 보내보도록 하겠습니다.
사전 설정
IMAP 설정 : 링크로 들어가서 IMAP 1단계 설정을 해줍니다. 이로서 다른 이메일 클라이언트에서 Gmail 을 사용할 수 있도록 해줍니다.
보안수준 설정 : 위 설정만으로는raise SMTPSenderRefused(code, resp, from_addr
에러가 발생합니다. 제가 접근하는 방식이 보안수준이 낮기 때문에 위험을 느끼고 Gmail 에서 접근을 막는 것 입니다. 따라서 위 링크로 들어가서 보안 수준을 낮춰줘야 합니다.
다음으로는 장고 내부 settings.py
로 이동하여 설정을 해보도록 합시다.
# settings.py
EMAIL_HOST = 'smtp.gmail.com'
# 메일을 호스트하는 서버
EMAIL_PORT = '587'
# gmail과의 통신하는 포트
EMAIL_HOST_USER = '********@gmail.com'
# 발신할 이메일
EMAIL_HOST_PASSWORD = '********'
# 발신할 메일의 비밀번호
EMAIL_USE_TLS = True
# TLS 보안 방법
DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
# 사이트와 관련한 자동응답을 받을 이메일 주소,'webmaster@localhost'
메일 보내기
우선 테스트용 메일을 보내보도록 하겠습니다. 장고 쉘을 켜보도록 하겠습니다.
python manage.py shell
그리고 메일을 보내는 건 간단합니다.
>>> from django.core.mail import EmailMessage
>>> email = EmailMessage('title', 'content', to=['아이디@서버의 주소'])
>>> email.send()
1
>>>
1
은 메일이 성공적으로 보내졌을 때의 return 값 입니다. 메일은 title
이라는 제목과 content
라는 내용으로 to
에게 보내지게 됩니다.
회원가입 시 인증메일 보내기
메일을 보낼 수 있다고 해서 인증절차가 끝난 건 아닙니다. 이 외에도 알아야 할 것이 많지만 함께 구현하면서 알아보도록 하겠습니다.
저번 시간에 알아봤던 로그인 기능 유지하기
에서 응용한 코드니 이해가 잘 안된다면, 보고 오셔도 좋습니다. 그 외에도 import 해오는 메소드들이 많습니다.
# views.py
# 기존에 사용하던 메소드
from django.shortcuts import render,redirect,HttpResponse
from django.contrib.auth.models import User
from django.contrib.auth.hashers import check_password
from django.contrib.auth import get_user_model
from django.contrib import auth
from ..models import Profile
# 새로 추가하는 메소드
from django.contrib.sites.shortcuts import get_current_site
from django.template.loader import render_to_string
rom django.utils.http import urlsafe_base64_encode,urlsafe_base64_decode
from django.utils.encoding import force_bytes
from django.core.mail import EmailMessage
from .tokens import account_activation_token
from django.utils.encoding import force_bytes, force_text
메소드들은 해당 메소드가 사용될 시에 알아보도록 하고, 전체 코드를 조금씩 나눠서 보도록 하겠습니다.
def signup(request):
if request.method == "POST":
if request.POST["password1"] == request.POST["password2"]:
user = User.objects.create_user(
username=request.POST["username"],
password=request.POST["password1"])
user.is_active = False
user.save()
nickname = request.POST["nickname"]
profile = Profile(user=user, nickname=nickname)
profile.save()
회원가입 시 이메일(유저아이디), 비밀번호, 비밀번호 확인, 닉네임을 입력하게 됩니다. 비밀번호가 같음을 체크하고 같다면 이메일과 비밀번호로 user를 생성합니다. 이 떄 user는 장고에서 기본 제공해주는 auth.User 입니다. 그리고 is_active
속성을 False 로 해주었습니다. 아직 이메일 인증이 안되었기 때문에 활성화를 꺼주는 것이죠. 다음으로는 닉네임을 받아 위에서 생성한 user 와 매칭 되는 profile 을 만들어주었습니다. 이는auth.User 와 onetoone
관계로서 장고의 auth.User 를 커스터마이징하기 위해 만들어주었습니다.
current_site = get_current_site(request)
# localhost:8000
message = render_to_string('account/user_activate_email.html', {
'user': user,
'domain': current_site.domain,
'uid': urlsafe_base64_encode(force_bytes(user.pk)).encode().decode(),
'token': account_activation_token.make_token(user),
})
-
get_current_site
- request를 보낸 site 를 알려줍니다.
- 이를 templates 에 넘겨서 이를 통해 url에 동적으로접근하도록 합니다.
-
render_to_string
- template 객체를 반환함과 동시에 render 합니다.
- 뒤에는 rendering 할 때 사용할 context 가 위치합니다.
다음으로는 user.pk
를 암호화하는 과정을 알아보도록 하겠습니다.
현재 user.pk 는 자연수값입니다. 이를 force_byte
를 통해 bytes 로 바꿔줍니다. 다음으로 urlsafe_base64_encode
로 인코딩해주고 이를 다시 decode
메소드를 통해 bytes에서 str 로 바꿔줍니다. 예를 통해 살펴보면 다음과 같습니다.
user.pk: 57 force_bytes(user.pk): b'57' urlsafe_base64_encode(force_bytes(user.pk)): b'NTc' urlsafe_base64_encode(force_bytes(user.pk)).decode(): NTc
이제 tokens
입니다. tokens 도 model 의 field에 추가해주고 위와 같은 과정을 똑같이 거쳐서 만들 수도 있지만 조금 다른 방식으로 만들어주었습니다. tokens.py
를 만들고 token 을 만드는 class 를 작성해주었습니다.
# tokens.py
from django.contrib.auth.tokens import PasswordResetTokenGenerator
from django.utils import six
class AccountActivationTokenGenerator(PasswordResetTokenGenerator):
def _make_hash_value(self, user, timestamp):
return (six.text_type(user.pk) + six.text_type(timestamp)) + six.text_type(user.is_active)
account_activation_token = AccountActivationTokenGenerator()
장고 3버젼부터 six를 지원해주지 않아서 설치 후 사용해야합니다.
pip install six
그 후 six를 settings.py의 INSTALLED_APPS에 넣어주고 다음과 같이 사용할 수 있습니다.
from django.contrib.auth.tokens import PasswordResetTokenGenerator
from six import text_type
class AccountActivationTokenGenerator(PasswordResetTokenGenerator):
def _make_hash_value(self, profile, timestamp):
return (text_type(profile.id)) + text_type(timestamp)
account_activation_token = AccountActivationTokenGenerator()
만들어준 AccountActivationTokenGenerator
는 PasswordResetTokenGenerator
를 상속받고 있습니다. PasswordResetTokenGenerator
는 장고에서 제공해주는 class 로서 비밀번호를 리셋할 떄 token을 발급해주고 해당 기능을 처리해줍니다. 우리는 이 방법을 빌려 토큰을 받겠습니다.
text_type
은 유니코드 정수로부터 유니코드 문자열을 가져옵니다. user의 pk, 현재시간, user의 활성화를 가지고 합쳐 tokens 을 생성해줍니다.
다시 views
로 넘어오도록 하죠.
mail_subject = "[SOT] 회원가입 인증 메일입니다."
user_email = user.username
email = EmailMessage(mail_subject, message, to=[user_email])
email.send()
return HttpResponse(
'<div style="font-size: 40px; width: 100%; height:100%; display:flex; text-align:center; '
'justify-content: center; align-items: center;">'
'입력하신 이메일<span>로 인증 링크가 전송되었습니다.</span>'
'</div>'
)
return redirect('account:home')
return render(request, 'account/signup.html')
위에서 실습해봤던 이메일 보내는 과정입니다. 자세한 설명은 생략하도록 하겠습니다.
email 로 도착하게 되는 user_active_email
입니다.
{% autoescape off %}
안녕하세요
아래 링크를 클릭하면 회원가입 인증이 완료됩니다.
회원가입 링크 : http://{{ domain }}{% url 'account:activate' uid64=uid token=token %}
{% endautoescape %}
autoescape
는 html 을 그대로 화면에 보여주게 됩니다. 그래서 이를 off 로 꺼주도록 합시다. 만일 켜져있다면 "
이 "
다음과 표시되는 등 문제가 발생할 것입니다. active
라는 이름의 url로 접속하게 되면 views/activate
가 실행됩니다.
# views.py
def activate(request, uid64, token):
uid = force_text(urlsafe_base64_decode(uid64))
user = User.objects.get(pk=uid)
if user is not None and account_activation_token.check_token(user, token):
user.is_active = True
user.save()
auth.login(request, user)
return redirect('account:home')
else:
return HttpResponse('비정상적인 접근입니다.')
암호화하였던 user의 id 를 다시 decode 함으로서 해당 user를 불러옵니다. 또한 위에서 언급했던 PasswordResetTokenGenerator
안에 있는 check_token
으로 유효성을 검사해줍니다. 해당 user가 존재하고 token도 유효하다면 active 를 활성화 해주고 로그인 해줍니다.
'웹프로그래밍 > Django' 카테고리의 다른 글
[Django]humanize 를 이용해 세 자리마다 콤마찍기 (0) | 2019.05.21 |
---|---|
[Django] 아임포트를 이용한 결제모듈 만들기 (0) | 2019.05.20 |
[Django]로그인 유지하기, 쿠키와 세션 (4) | 2019.05.09 |
[Django]User 비밀번호 변경하기, check_password (0) | 2019.05.04 |
[Django] OneToOne 방법으로 User Model 확장 (7) | 2019.05.04 |