social login 기능을 구현하기 위해 django기본 모델이 아닌 rest-auth 패키지를 이용하기로 했다.
social 로그인을 위해 django-allauth 라이브러리를 이용해야 하는데, 내가 만든 장고 유저랑 동시에 구현하기에 어려움이 많아 기본 유저 기능도 싹 다 갈아엎었다.
나는 프론트와 데이터를 주고 받아야 하고, rest-auth라이브러리가 다양한 기능을 제공하기에 django-rest-auth 라이브러리를 설치하고 만들었다. 처음 계획은 rest-auth(회원가입 로그인)과 allauth(소셜 로그인)+simplejwt를 이용할 계획이었다.
#models.py
class User(AbstractUser):
first_name=None
last_name=None
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
deleted_at = models.DateTimeField(null=True)
email = models.EmailField(unique=True)
username = models.CharField(unique=True, default='', max_length=256)
nickname = models.CharField(max_length=8)
password=models.CharField(max_length=128)
url_value = models.CharField(max_length=2000)
def __str__(self):
return f'{self.username}'
처음 생각한 user model이다. 이 model에서 좀 많이 바뀌게 되는데 아래에서 설명한다...
설정
필요한 라이브러리들을 설치해준다.
pip install djangorestframework
pip install django-rest-auth(업데이트되면서 dj-rest-auth가 필요한 경우도 있음)
pip install django-allauth
pip install djangorestframework-simplejwt(jwt업데이트가 더이상 진행이 안돼서 simplejwt를 사용함)
rest-auth에서는 기본적으로 email, username, password를 필드로 사용한다.
#settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'accounts',
'cheers',
'charms',
#django-rest-auth, django-allauth
'rest_framework.authtoken',
'rest_auth',
# 'django.contrib.sites', social 로그인과 병행할 경우 이게 설치되어 있으면 오류가 뜬다
'allauth',
'allauth.account',
'allauth.socialaccount',
'rest_auth.registration',
# provider
'allauth.socialaccount.providers.kakao', #카카오 소셜로그인
]
#user model
AUTH_USER_MODEL = 'accounts.User'
SITE_ID=1
REST_USE_JWT = True #JWT인증방식 사용
# ACCOUNT_USER_MODEL_USERNAME_FIELD = None
ACCOUNT_EMAIL_REQUIRED = False #이메일 필드 x
ACCOUNT_UNIQUE_EMAIL = False
ACCOUNT_UNIQUE_USERNAME= True
ACCOUNT_USERNAME_REQUIRED = True #username 필드(아이디) ㅇ
ACCOUNT_AUTHENTICATION_METHOD = 'username'
ACCOUNT_EMAIL_VERIFICATION = 'none' #이메일 인증x
# ACCOUNT_CONFIRM_EMAIL_ON_GET = True
# ACCOUNT_EMAIL_CONFIRMATION_ANONYMOUS_REDIRECT_URL = '/?verification=1'
# ACCOUNT_EMAIL_CONFIRMATION_AUTHENTICATED_REDIRECT_URL = '/?verification=1'
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' #이메일 인증을 위함.
REST_AUTH_SERIALIZERS = {
'REGISER_SERIALIZER': 'accounts.serializers.UserSerializer',
}
REST_AUTH_REGISTER_SERIALIZERS = {
'REGISTER_SERIALIZER': 'accounts.serializers.CustomRegisterSerializer',
}
ACCOUNT_ADAPTER = 'accounts.adapter.CustomAccountAdapter'
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
'rest_framework_simplejwt.authentication.JWTAuthentication',
],
}
뭐가 많은데, 여기서 중요한 점은 내가 필요한 라이브러리들을 앱에다가 작성해주고, 인증방식 설정과 유저 모델 커스텀을 위한 serializer, adapter부분이다. 기본으로 제공하는 모델들은 제거하기 어렵고, 대신 필요없는 이메일 필드는 false로 처리하여 받지 않아도 되도록 설정했다. 나는 email필드가 필요없고 nickname과 url_value를 추가로 받아야 하기 때문에 많이 고민했다. 또, 나는 이메일 인증까지는 필요없어 none으로 설정함.
다음으로 url을 설정한다.
#urls.py
from django.contrib import admin
from django.urls import path, include
from django.urls import re_path as url
from accounts.views import *
urlpatterns = [
path('admin/', admin.site.urls),
url(r'^rest-auth/', include('rest_auth.urls')),
url(r'^rest-auth/registration/', include('rest_auth.registration.urls')),
#소셜로그인
path('accounts/', include('allauth.urls')),
path('', include('django.contrib.auth.urls')),
]
이렇게 하면 rest-auth가 제공하는 기능들을 사용할 수 있다.
https://django-rest-auth.readthedocs.io/en/latest/api_endpoints.html
API endpoints — django-rest-auth 0.9.5 documentation
username first_name last_name Returns pk, username, email, first_name, last_name
django-rest-auth.readthedocs.io
기능은 여기에 자세히 나와있따.
회원가입
rest-auth를 사용할 때 가장 큰 문제는 내가 커스텀한 유저 모델을 사용할 수 없는 것이였다.
직접 serializers.py와 adpater.py에 사용할 필드들을 커스텀해준다. 필요한 세팅은 앞에서 settings.py에 입력 완료 했다.
#serializers.py
from rest_auth.registration.serializers import RegisterSerializer
class CustomRegisterSerializer(RegisterSerializer):
nickname = serializers.CharField(
required=True,
max_length=8,
)
def get_cleaned_data(self):
data_dict = super().get_cleaned_data()
data_dict['nickname'] = self.validated_data.get('nickname', '')
return data_dict
실질적으로 유저에게 입력받아야 하는 필드는 nickname필드만 있다. get_cleam_data(self)함수에는 원래 email, username, password1, password2만 받도록 되어있지만, 이를 상속받아 nickname도 추가시켜준다.
#adapter.py
from allauth.account.adapter import DefaultAccountAdapter
from django.utils.crypto import get_random_string
class CustomAccountAdapter(DefaultAccountAdapter):
def save_user(self, request, user, form, commit=False):
RANDOM_STRING_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
user = super().save_user(request, user, form, commit)
data = form.cleaned_data
user.nickname = data.get('nickname')
user.url_value=get_random_string(length=10, allowed_chars=RANDOM_STRING_CHARS)
user.save()
return user
adpater함수도 마찬가지로 상속받은 뒤 user데이터에 저장할 필드들을 처리해준다. url_value값은 10개의 랜덤문자열을 저장하기 위해 get_random_string함수를 사용하였다.
**참고**
class RegisterSerializer(serializers.Serializer):
username = serializers.CharField(
max_length=get_username_max_length(),
min_length=allauth_settings.USERNAME_MIN_LENGTH,
required=allauth_settings.USERNAME_REQUIRED
)
email = serializers.EmailField(required=allauth_settings.EMAIL_REQUIRED)
password1 = serializers.CharField(write_only=True)
# password2 = serializers.CharField(write_only=True)
def validate_username(self, username):
username = get_adapter().clean_username(username)
return username
def validate_email(self, email):
email = get_adapter().clean_email(email)
if allauth_settings.UNIQUE_EMAIL:
if email and email_address_exists(email):
raise serializers.ValidationError(
_("A user is already registered with this e-mail address."))
return email
def validate_password1(self, password):
return get_adapter().clean_password(password)
# def validate(self, data):
# if data['password1'] != data['password2']:
# raise serializers.ValidationError(_("The two password fields didn't match."))
# return data
def custom_signup(self, request, user):
pass
def get_cleaned_data(self):
return {
'username': self.validated_data.get('username', ''),
'password1': self.validated_data.get('password1', ''),
'email': self.validated_data.get('email', '')
}
def save(self, request):
adapter = get_adapter()
user = adapter.new_user(request)
self.cleaned_data = self.get_cleaned_data()
adapter.save_user(request, user, self)
self.custom_signup(request, user)
setup_user_email(request, user, [])
return user
rest-auth 패키지의 RegisterSerializer의 일부 코드를 가져왔다. 이걸 참고하면 위해 커스텀한 함수와 함께 함수가 어떻게 돌아가는 지 알 수 있을 것이다.(참고로 나머지는 social 관련 함수들...)
또, 원래 회원가입 시에 password1과 password2를 입력받아 둘이 같은지 비교한 뒤 회원가입을 하는 절차가 있었는데, 필요없어서 지웠다.
내가 설정한 필드들이 잘 나온다. email은 작성안해도 회원가입이 성공적으로 되지만, 이전의 email과 중복되면 안된다.
설정을 했지만 적용이 안된다. user는 unique설정이 잘 적용되는데 왜일까?? 아무튼 성공적으로 회원가입이 완료되면
이렇게 뜬다.
여기서도 의문점이 있다. 처음에 created, deleted, update시간 설정을 하고 싶었으나, model을 바로 직렬화하지 못하고 serializers.py에서 직접 건드리다보니, datetimefield의 auto_now기능을 사용하지 못했고 결국 위 시간과 관련된 기능들은 삭제했다. 근데 회원가입을 완료하면 정상적으로 created_at값이 return 되는데, 어디서 받아오는 함수가 있는지 모르겠다. 물론 user model에는 created_at필드가 있지만, 그러면 왜 다른 값들은 안받아오고 created_at만 받아오는걸까??
model에서 어디까지 설정해야 rest-auth의 기본 유저모델에 적용되는지 모르겠다.
last_name=None등의 설정은 잘 적용되는 것 같은데....
아직 어렵다.
작성하다보니까 너무 길어져서 로그인, 로그아웃 기능을 따로 나누어야될듯.
참고
**기본 설정
https://blog.naver.com/code4human/222006324011
[Django] Django REST framework(DRF) 환경에서 로그인 구현 기록
프로젝트를 위해 Django REST Framework(이하 DRF)를 사용하는 환경에서 로그인 기능을 구현하게 ...
blog.naver.com
**rest-auth 커스텀 모델 관련
django-rest-auth custom registration fails to save extra fields
I am using DRF and for login/registration I am using Django-rest-auth. I have customized User model to have extra fields I have custom registration serializer to store extra fields along with use...
stackoverflow.com
How to save extra fields on registration using custom user model in DRF + django-rest-auth
Using Django REST Framework (DRF), with django-rest-auth, I created a custom user model with one extra field. My aim is to use the django-rest-auth registration endpoint to register a new user in one
stackoverflow.com
**인증 및 여러가지
https://han-py.tistory.com/216
Django rest framework_3. 회원관리(로그인, 회원가입)
0. 들어가면서 Django rest framework_1, 2를 보고 오는 것을 추천하다. 이제 앞에서 포스트맨으로 확인을 했으니 check의 Create에 없는 User를 완성하기 위해 accounts 부분을 건드려 보자. 1. 주의사항 아래의
han-py.tistory.com
**django-rest-auth 업데이트 관련 및 설정 변경
ImportError: cannot import name 'url' from 'django.conf.urls' django-rest-auth
error : from django.conf.urls import url ImportError: cannot import name 'url' from 'django.conf.urls' -version Django==4.0.1 django-rest-auth==0.9.5 Pl help me.Thank you in advance url.py # ...
stackoverflow.com
django 최신버전에서 이 부분 수정 안하면 오류 생긴다!!! 오류난 파일 경로는 VSC가 알려주니 거기로 들어가 하나하나 수정해주자.
이런식으로 수정하면 된다!
'멋사 10기 졸업 프로젝트 > 정리' 카테고리의 다른 글
[Django] django rest-auth패키지를 이용한 유저 기능[2] - login,logout(로그인, 로그아웃) (0) | 2023.01.22 |
---|---|
[django] password 암호화 : make_password, check_password (0) | 2023.01.10 |
[django] simple-jwt방식을 이용한 login, logout 기능 구현 (1) | 2023.01.01 |