많은 포털 사이트에서 회원가입 시 웹 메일 인증 시스템을 채택하고 있다.
(현재는 대부분 보안성 때문에 기기 인증 등도 활용되고 있다고 한다...)
프로젝트를 진행하면서 해당 부분이 필요하게 되어 찾아보며 정리해봤다.
진행되는 로직은 크게 SMTP를 활용하여, User의 is_actvie 속성을 활용한다.
SMTP 웹 메일을 통해 토큰을 날려주고, 해당 토큰을 인증하기 전 까지 is_active 속성을 False로 둔다.
그리고 토큰을 통해 인증을 하는 순간 is_active 속성을 True로 변경하여 User 객체를 인증한다.
(즉, 토큰을 SMTP 프로토콜을 통해 날려준다)
# SMTP
간이 전자 우편 전송 프로토콜(Simple Mail Transfer Protocol, SMTP)은 인터넷에서 이메일을 보내기 위해 이용되는 프로토콜이다. 쉽게 말해, 간단히 두 메일 서버 간의 통신을 지원해주는 프로토콜로 우리는 SMTP를 통해 회원가입 관련 인증 메일을 전송할 것이다.
# 선행 설정
웹메일 SMTP를 활용하기 위해서는 다음과 같이 두 가지의 설정이 필요하다
- IMAP 사용함 : IMAP은 인터넷을 통해 메일 서버에 접근하기 위한 프로토콜로 우리는 이 프로토콜을 사용해서 처리한다
- 보안수준 낮은 앱 허용 : 우리는 Django 서버를 통해 보안수준이 낮은 형태로 접근하기에 해당 설정을 허용해야 한다
# Django SMTP 설정
# settings.py
EMAIL_HOST = 'smtp.gmail.com' # 메일 호스트 서버
EMAIL_PORT = '587' # 서버 포트
EMAIL_HOST_USER = 'ID@gmail.com' # 우리가 사용할 Gmail
EMAIL_HOST_PASSWORD = 'pw' # 우리가 사용할 Gmail p
EMAIL_USE_TLS = True # TLS 보안 설정
DEFAULT_FROM_EMAIL = EMAIL_HOST_USER # 응답 메일 관련 설정
# Shell 통해 확인하기
$ python manage.py shell
>>> from django.core.mail import EmailMessage
>>> email = EmailMessage('title', 'content', to=['id@gmail.com'])
>>> email.send()
다음의 결과 값으로 1이 들어오면 정상적으로 발송된 것이다.
실제 메일이 수신됨을 확인할 수 있다.
# Singup Template에 Email 입력칸 추가하기
<h1>Sign Up!</h1>
<form method="POST" action="{% url 'signup'%}">
{% csrf_token %}
Username:
<br>
<input name="username" type="text" value="">
<br>
Email:
<br>
<input name="email" type="email" value="">
<br>
Password:
<br>
<input name="password1" type="password" value="">
<br>
Confirm Password:
<br>
<input name="password2" type="password" value="">
<br>
<br>
<input class="btn btn-primary" type="submit" value="Sign Up!">
</form>
# View.py 코드 변경
/project/accounts/views.py
from django.shortcuts import render, redirect
from django.contrib.auth.models import User
from django.contrib import auth
# SMTP 관련 인증
from django.contrib.sites.shortcuts import get_current_site
from django.template.loader import render_to_string
from django.utils.http import urlsafe_base64_encode,urlsafe_base64_decode
from django.core.mail import EmailMessage
from django.utils.encoding import force_bytes, force_text
from .tokens import account_activation_token
# Create your views here.
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()
current_site = get_current_site(request)
message = render_to_string('accounts/activation_email.html', {
'user': user,
'domain': current_site.domain,
'uid': urlsafe_base64_encode(force_bytes(user.pk)),
'token': account_activation_token.make_token(user),
})
mail_title = "계정 활성화 확인 이메일"
mail_to = request.POST["email"]
email = EmailMessage(mail_title, message, to=[mail_to])
email.send()
return redirect("home")
# 포스트 방식 아니면 페이지 띄우기
return render(request, 'accounts/signup.html')
def login(request):
# 포스트 방식으로 들어오면
if request.method == 'POST':
# 정보 가져와서
username = request.POST['username']
password = request.POST['password']
# 로그인
user = auth.authenticate(request, username=username, password=password)
# 성공
if user is not None:
auth.login(request, user)
return redirect('home')
# 실패
else:
return render(request, 'accounts/login.html', {'error': 'username or password is incorrect.'})
else:
return render(request, 'accounts/login.html')
def logout(request):
# 포스트 방식으로 들어오면
if request.method == 'POST':
# 유저 로그아웃
auth.logout(request)
return redirect('home')
return render(request, 'accounts/signup.html')
# 계정 활성화 함수(토큰을 통해 인증)
def activate(request, uidb64, token):
try:
uid = force_text(urlsafe_base64_decode(uidb64))
user = User.objects.get(pk=uid)
except(TypeError, ValueError, OverflowError, User.DoesNotExsit):
user = None
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("home")
else:
return render(request, 'home.html', {'error' : '계정 활성화 오류'})
return
# Tokens.py 파일 추가하기
여기서 약간의 문제가 있었다.
처음에는 six Library를 활용하여 토큰을 만들어내는 코드들이 활용되고 있었는데 계속 six 를 import할 수 없다고 에러가 발생하였다. 찾아보니 3.0에서 현재 지원하지 않는다고 하여 import를 할 수 없다고 한다...
그래서 six library 대신하여 str를 통해 단순히 pk 와 timestamp를 변경해주기로 하였다.
실제 서비스에서는 이렇게 구현하면 안될것 같다... user의 pk와 같은 주요데이터들이 노출된다던지, 겹친다던지 등의 문제 때문에 위험할 것 같다. 그렇지만 추후에 보완할 수 있도록 하고 지금은 이렇게 해결하였다.
/project/accounts/tokens.py 파일 생성
from django.contrib.auth.tokens import PasswordResetTokenGenerator
class AccountActivationTokenGenerator(PasswordResetTokenGenerator):
def _make_hash_value(self, user, timestamp):
return (str(user.pk) + str(timestamp)) + str(user.is_active)
account_activation_token = AccountActivationTokenGenerator()
# 웹 메일 인증 템플릿 생성
project/accounts/templates/account/activation_email.html 파일 생성
{{user.username}}님, 아래 링크를 클릭하여 계정을 활성화하세요:
http://{{domain}}{% url 'activate' uid token %}
# Accounts의 url 추가하기
project/accounts/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('signup/', views.signup, name="signup"),
path('login/', views.login, name="login"),
path('logout/', views.logout, name="logout"),
path('activate/<str:uidb64>/<str:token>/', views.activate, name="activate"),
]
# 결과
참조
https://ssungkang.tistory.com/105#comment5657240
'Web Programming > Django' 카테고리의 다른 글
Ajax 통신하기 (0) | 2020.02.29 |
---|---|
Static 추가하기 (0) | 2020.02.16 |
Like 좋아요 모델 추가하기 (1) | 2019.06.25 |
백엔드_CRUD 실습 3 (0) | 2019.05.13 |
백엔드_CRUD 실습 2 (4) | 2019.05.10 |