이번 포스팅은 DRF로 회원가입/로그인 기능 구현이다.
요 기능들은 한 번 정리하면 나중에 도움될 듯하여 정리한다
$ pip install djangorestframework-jwt
$ pip install django-rest-authtoken
# settings.py
INSTALLED_APPS = [
...,
'rest_framework.authtoken',
...
]
AUTH_USER_MODEL = 'my_user.User'
## DRF
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated', # 인증된 사용자만 접근 가능
'rest_framework.permissions.IsAdminUser', # 관리자만 접근 가능
'rest_framework.permissions.AllowAny', # 누구나 접근 가능
),
'DEFAULT_RENDERER_CLASSES': (
# 자동으로 json으로 바꿔줌
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
# 'rest_framework.authentication.TokenAuthentication',
# 'rest_framework.authentication.SessionAuthentication',
# 'rest_framework.authentication.BasicAuthentication',
),
}
## JWT
# 추가적인 JWT_AUTH 설젇
JWT_AUTH = {
'JWT_SECRET_KEY': SECRET_KEY,
'JWT_ALGORITHM': 'HS256', # 암호화 알고리즘
'JWT_ALLOW_REFRESH': True, # refresh 사용 여부
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7), # 유효기간 설정
'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=28), # JWT 토큰 갱신 유효기간
# import datetime 상단에 import 하기
}
이 프로젝트에서 my_user 앱을 생성해 my_user/models.py에 User 모델을 작성하겠다.
AbstractBaseUser 상속 👉 password 컬럼 필요 없음AUTH_USER_MODEL = 'my_user.User' 추가# my_user/models.py
from django.db import models
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser, PermissionsMixin
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
class UserManager(BaseUserManager):
use_in_migrations = True
def _create_user(self, email, password, **extra_fields):
if not email:
raise ValueError('The given email must be set')
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_user(self, email, password=None, **extra_fields):
extra_fields.setdefault('is_staff', False)
extra_fields.setdefault('is_superuser', False)
return self._create_user(email, password, **extra_fields)
def create_superuser(self, email, password, **extra_fields):
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
if extra_fields.get('is_staff') is not True:
raise ValueError('Superuser must have is_staff=True.')
if extra_fields.get('is_superuser') is not True:
raise ValueError('Superuser must have is_superuser=True.')
return self.create_user(email, password, **extra_fields)
class User(AbstractBaseUser, PermissionsMixin):
"""
customized User
"""
email = models.EmailField(
verbose_name=_('email id'),
max_length=64,
unique=True,
help_text='EMAIL ID.'
)
username = models.CharField(
max_length=30,
)
is_staff = models.BooleanField(
_('staff status'),
default=False,
help_text=_('Designates whether the user can log into this admin site.'),
)
is_active = models.BooleanField(
_('active'),
default=True,
help_text=_(
'Designates whether this user should be treated as active. '
'Unselect this instead of deleting accounts.'
),
)
date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
objects = UserManager()
EMAIL_FIELD = 'email'
USERNAME_FIELD = 'email'
class Meta:
verbose_name = _('user')
verbose_name_plural = _('users')
def __str__(self):
return self.username
def get_short_name(self):
return self.email
User = get_user_model()
class UserCreateSerializer(serializers.Serializer):
email = serializers.EmailField(required=True)
username = serializers.CharField(required=True)
password = serializers.CharField(required=True)
print(email)
def create(self, validated_data):
user = User.objects.create( # User 생성
email=validated_data['email'],
username=validated_data['username'],
)
user.set_password(validated_data['password'])
user.save()
return user
@api_view(['POST'])
@permission_classes([AllowAny]) # 인증 필요없다
def signup(request):
serializer = UserCreateSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
serializer.save() # DB 저장
return Response(serializer.data, status=201)
처음에 클래스 내에 함수 정의했다가... 계속 UnAuthorized(401) 코드 나와서 그냥 밖으로 빼내서
@permission_classes([AllowAny]) 설정했더니 되었다
회원가입 기능은 완료되었다.
User 모델 작성하는 것만 제대로 할 수 있다면 나머지는 수월 !~!
다만 나는 @permission_classes([AllowAny]) 요 속성을 이번에 알아서 매우 헤맸..
로그인을 할 때는, JWT를 이용해 토큰 발행까지 진행 !
User = get_user_model()
JWT_PAYLOAD_HANDLER = api_settings.JWT_PAYLOAD_HANDLER
JWT_ENCODE_HANDLER = api_settings.JWT_ENCODE_HANDLER
class UserLoginSerializer(serializers.Serializer):
email = serializers.CharField(max_length=64)
password = serializers.CharField(max_length=128, write_only=True)
token = serializers.CharField(max_length=255, read_only=True)
def validate(self, data):
email = data.get("email", None)
password = data.get("password", None)
user = authenticate(email=email, password=password)
if user is None:
return {
'email': 'None'
}
try:
payload = JWT_PAYLOAD_HANDLER(user)
jwt_token = JWT_ENCODE_HANDLER(payload) # 토큰 발행
update_last_login(None, user)
except User.DoesNotExist:
raise serializers.ValidationError(
'User with given email and password does not exists'
)
return {
'email': user.email,
'token': jwt_token
}
@api_view(['POST'])
@permission_classes([AllowAny])
def login(request):
if request.method == 'POST':
serializer = UserLoginSerializer(data=request.data)
if not serializer.is_valid(raise_exception=True):
return Response({"message": "Request Body Error."}, status=status.HTTP_409_CONFLICT)
if serializer.validated_data['email'] == "None": # email required
return Response({'message': 'fail'}, status=status.HTTP_200_OK)
response = {
'success': True,
'token': serializer.data['token'] # 시리얼라이저에서 받은 토큰 전달
}
return Response(response, status=status.HTTP_200_OK)
my_user/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('signup', views.signup),
path('login', views.login),
]
DRF에 회원가입, 로그인 기능을 적용하는 건... 한 3시간? 정도 걸린 듯하다
계속해서 401 코드가 응답되고, csrf 에러, migration 에러 등등..
처음에 확장 모델이 아닌 그냥 처음에 User 모델 자체를 첨부터 만들어서 authentication 적용하는 법도 엄청 어려웠다.
그래서 다 지우고, 새로 my_user app 생성해서 만들고,, 엄청 많은 수정을 거쳐 성공했다
(여기서 db.sqlite3, migrations 폴더 지우고 다시 makemigrations 명령어를 치면 된다는 것도 알게됨 구글링 최고 ㅎㅎ)
어서 빨리 Post, Comment 다시 User 외래키로 넣어서 테스트해봐야지
DRF 꽤 재밌네 😎
감사합니다.