공식문서를 보면서 jwt와 user를 사용해보려고 한다.
여기서 소셜인증은 사용하지 않는다.
환경세팅
1. 패키지 설치
pip install djangorestframework
pip install djangorestframework-simplejwt
pip install django-allauth
pip install dj-rest-auth
2. settings.py
# settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'rest_framework.authtoken',
'rest_framework_simplejwt.token_blacklist',
'allauth',
'allauth.account'
'dj_rest_auth',
'dj_rest_auth.registration',
# app
'bwh',
'accounts',
]
SITE_ID = 1
AUTH_USER_MODEL = 'accounts.User'
...
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'dj_rest_auth.jwt_auth.JWTCookieAuthentication',
)
}
REST_USE_JWT = True
여기까지 설정을 dj-rest-auth문서 dj-rest-auth 공식문서 링크를 보고 설정할 수 있다.AUTH_USER_MODEL
은 accounts앱의 models.py에서 User를 사용하겠다고 설정한 부분이다.
다음으로 django-allauth 설정을 이어서 한다.
# settings.py
ACCOUNT_USER_MODEL_USERNAME_FIELD = None
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_USERNAME_REQUIRED = False
ACCOUNT_AUTHENTICATION_METHOD = 'email'
django-allauth를 보고 작성했다.
작성한 명령어들의 설명은 다음과 같다.
ACCOUNT_USER_MODEL_USERNAME_FIELD
: 커스텀 사용자 모델을 사용하는 경우 아이디 필드의 이름이 username이 아닌 다른 이름일 경우 지정한다.
만약 None으로 지정할 경우 allauth에서 username과 관련된 모든 기능을 사용하지 않는다.
이 경우 ACCOUNT_USERNAME_REQUIRED 값 또한 반드시 False로 지정해야 한다.ACCOUNT_EMAIL_REQUIRED
: 회원가입할 때 이메일 주소 입력 필수 여부이다. 디폴트 값은 False이다.ACCOUNT_USERNAME_REQUIRED
: 회원 가입할 때 username 입력 필수 여부이다. 디폴트 값은 True이므로 반드시 ACCOUNT_AUTHENTICATION_METHOD를 통해 이메일로 로그인으로 설정하더라도 username을 입력해야 가입된다.ACCOUNT_AUTHENTICATION_METHOD
: 로그인 인증 방법으로 username, email, username_email을 지정할 수 있다. email로 설정할 때는 ACCOUNT_EMAIL_REQUIRED = True 옵션을 같이 설정해야 한다.
# settings.py
from datetime import timedelta
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
'ROTATE_REFRESH_TOKENS': False,
'BLACKLIST_AFTER_ROTATION': False,
'UPDATE_LAST_LOGIN': False,
'ALGORITHM': 'HS256',
'SIGNING_KEY': SECRET_KEY,
'VERIFYING_KEY': None,
'AUDIENCE': None,
'ISSUER': None,
'JWK_URL': None,
'LEEWAY': 0,
'AUTH_HEADER_TYPES': ('Bearer',),
'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
'USER_ID_FIELD': 'id',
'USER_ID_CLAIM': 'user_id',
'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule',
'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
'TOKEN_TYPE_CLAIM': 'token_type',
'TOKEN_USER_CLASS': 'rest_framework_simplejwt.models.TokenUser',
'JTI_CLAIM': 'jti',
'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp',
'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5),
'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1),
}
simple-jwt를 보고 작성했다.
5번 라인과, 6번 라인을 통해 토큰의 유효기간을 정할 수 있다.
3. urls.py
# urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('bwh/', include('bwh.urls')),
path('accounts/', include('accounts.urls')),
]
app Setting
1. app/models.py
# accounts/models.py
from django.db import models
from django.contrib.auth.models import AbstractBaseUser
class User(AbstractBaseUser):
email = models.EmailField('EMAIL', max_length=100, null=False, blank=False, unique=True)
nickname = models.CharField('NICKNAME', max_length=100, null=False, blank=False)
profile_image = models.ImageField('PROFILE_IMG', upload_to='user/%Y/%m', blank=True, null=True)
USERNAME_FIELD = 'email'
def __str__(self):
return self.nickname
class Meta:
db_table = 'accounts'
회원가입에서는 AbstractBaseUser를 사용한다.
AbstractBaseUser는 간단하게 설명하면 username 컬럼을 사용 안할 때 상속하여 사용한다.
AbstractUser는 username 컬럼을 사용할 때 상속한다.
2. app/serializers.py
# accounts/serializers.py
from rest_framework import serializers
from dj_rest_auth.registration.serializers import RegisterSerializer
from rest_framework.validators import UniqueValidator
from .models import User
class CustomRegisterSerializer(RegisterSerializer):
'''
회원가입 Custom
'''
username = None
nickname = serializers.CharField(required=True, validators=[UniqueValidator(queryset=User.objects.all(), message=("Name already exists"))])
profile_image = serializers.ImageField(use_url=True)
def get_cleaned_data(self):
data = super().get_cleaned_data()
data['profile_image'] = self.validated_data.get('profile_image', '')
data['username'] = self.validated_data.get('username')
return data
serializers에서 사용하는 field에 대한 설명은 drf 문서에 설명이 잘 나와 있다.
RegisterSerializer는 기본적으로 username, email, password만 받아준다. models.py에 추가한 nickname과 profile_image도 넣어주기 위해 get_cleaned_data를 오버라이딩 해준다.
그리고 custom을 했다면 settings.py에 serializers를 재정의 해줘야한다.
# settings.py
REST_AUTH_REGISTER_SERIALIZERS = {
'REGISTER_SERIALIZER': 'accounts.serializers.CustomRegisterSerializer',
}
3. app/adapter.py
RegisterSerializer를 상속받아 커스텀하였을 때, 추가한 필드들은 입력값이 저장이 안된다.
이 문제를 해결하기 위해서는 adapter도 수정해야 한다.
참고 : 참고 블로그
# accounts/adapter.py
from allauth.account.adapter import DefaultAccountAdapter
from allauth.account.utils import user_field
class CustomAccountAdapter(DefaultAccountAdapter):
def save_user(self, request, user, form, commit=True):
data = form.cleaned_data
user = super().save_user(request, user, form, False)
# 추가 필드
nickname = data.get('nickname')
profile_image = data.get('profile_image')
if nickname:
user.nickname = nickname
if profile_image:
user.profile_image = profile_image
user.save()
return user
allauth.account.adapter.DefaultAccountAdapter에서 save_user를 가져와서 오버라이딩을 해줬다.
4. app/urls.py
# accounts/urls.py
from django.urls import path, include
urlpatterns = [
path('', include('dj_rest_auth.urls')),
path('signup/', include('dj_rest_auth.registration.urls')),
]
결과
이제 로그인을 하면 토큰과 user의 pk, email이 나타난다.
'DevOps > Django' 카테고리의 다른 글
[Django] django extensions shell_plus 사용하기 (0) | 2022.05.16 |
---|---|
[Django] 장고 모델(ORM) (0) | 2022.05.11 |
[DRF] pagination custom (0) | 2022.03.24 |
[DRF] 직렬화, 역직렬화 (0) | 2022.03.23 |
[Django] 참고사이트 (0) | 2022.03.22 |