오늘은 요구사항 중에 유저기능 일부 작업함
setting.py
INSTALLED_APPS = [
...
'rest_framework',
]
# 라이브러리 추가 - REST_FRAMEWORK 부분이 없으면 그부분부터 전체 추가
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
)
}
# JWT 설정 - 잘 모르겠고 일단 필요해 보이는 것만 추가해 봄
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=30),
'REFRESH_TOKEN_LIFETIME': timedelta(days=7),
"ALGORITHM": "HS256",
"SIGNING_KEY": SECRET_KEY,
'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp',
'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5),
'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1),
urls.py
from django.urls import path
from users import views
from rest_framework_simplejwt.views import (
TokenObtainPairView,
TokenRefreshView,
)
urlpatterns = [
path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]
models.py
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import (
BaseUserManager, AbstractBaseUser
)
class UserManager(BaseUserManager):
def create_user(self, email, username, password=None):
""" 일반 유저 생성 """
if not email:
raise ValueError('Users must have an email address')
user = self.model(
email=self.normalize_email(email),
username=username,
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, username, password=None):
""" 슈퍼 유저 생성 """
user = self.create_user(
email,
username=username,
password=password,
)
user.is_admin = True
user.save(using=self._db)
return user
class User(AbstractBaseUser):
email = models.EmailField(
verbose_name='Email address',
max_length=255,
unique=True,
)
... # 필드 추가
objects = UserManager()
USERNAME_FIELD = 'username' # 로그인시 아이디로 사용할 필드
REQUIRED_FIELDS = []
def __str__(self):
return self.username
@property
def is_staff(self):
"Is the user a member of staff?"
return self.is_admin
admin.py
from django import forms
from django.contrib import admin
from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from django.core.exceptions import ValidationError
from users.models import User
class UserCreationForm(forms.ModelForm):
password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(
label='Password confirmation', widget=forms.PasswordInput)
class Meta:
model = User
fields = ('email','username',)
def clean_password2(self):
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise ValidationError("Passwords don't match")
return password2
def save(self, commit=True):
user = super().save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
class UserChangeForm(forms.ModelForm):
password = ReadOnlyPasswordHashField()
class Meta:
model = User
fields = ('email', 'username', 'password', 'is_active', 'is_admin',)
class UserAdmin(BaseUserAdmin):
form = UserChangeForm
add_form = UserCreationForm
# admin page 설정
list_display = ('email', 'username', 'is_admin') # 리스트에서 보여줄 필드
list_filter = ('is_admin',) # 필터 설정
fieldsets = (
(None, {'fields': ('email','username', 'password')}),
(('Personal info'), {'fields': ('nickname', 'fullname', 'date_of_birth')}),
('Permissions', {'fields': ('is_admin',)}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email', 'username', 'fullname', 'nickname', 'date_of_birth', 'password1', 'password2'),
}),
)
search_fields = ('email', 'username')
ordering = ('email',)
filter_horizontal = ()
admin.site.register(User, UserAdmin)
admin.site.unregister(Group)
serializer.py
from rest_framework import serializers
from users.models import User
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = "__all__"
def create(self, validated_data):
user = super().create(validated_data)
pw = user.password
user.set_password(pw)
user.save()
return user
urls.py
from django.urls import path
from users import views
from rest_framework_simplejwt.views import (
TokenObtainPairView,
TokenRefreshView,
)
urlpatterns = [
path('signup/', views.UserView.as_view(), name='user_view'), # post-회원가입
...
]
views.py
class UserView(APIView):
def post(self, request):
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response({"message": "register successs"}, status=status.HTTP_201_CREATED)
else:
return Response({"message": f"${serializer.errors}"}, status=status.HTTP_400_BAD_REQUEST)
urls.py
from django.urls import path
from users import views
from rest_framework_simplejwt.views import (
TokenObtainPairView,
TokenRefreshView,
)
urlpatterns = [
path('signup/', views.UserView.as_view(), name='user_view'), # post-회원가입
path('auth/', views.UserAuthView.as_view(),name='user_view'), # post-로그인/delete-로그아웃/get-유저확인
...
]
views.py
class UserAuthView(APIView):
# 로그인
def post(self, request):
# 유저 인증
user = authenticate(
username=request.data.get("username"), password=request.data.get("password")
)
# 이미 회원가입 된 유저일 때
if user is not None:
serializer = UserSerializer(user)
# jwt 토큰 접근
token = TokenObtainPairSerializer.get_token(user)
refresh_token = str(token)
access_token = str(token.access_token)
res = Response(
{
"user": serializer.data,
"message": "login success",
},
status=status.HTTP_200_OK,
)
# jwt 토큰 => 쿠키에 저장
res.set_cookie("access", access_token, httponly=True)
res.set_cookie("refresh", refresh_token, httponly=True)
return res
else:
return Response(status=status.HTTP_400_BAD_REQUEST)
# 로그아웃
def delete(self, request):
# 쿠키에 저장된 토큰 삭제 => 로그아웃 처리
response = Response({
"message": "Logout success"
}, status=status.HTTP_202_ACCEPTED)
response.delete_cookie("access")
response.delete_cookie("refresh")
return response
views.py
# 유저 정보 확인
def get(self, request):
try:
access = request.COOKIES['access'] # 쿠키에서 access token 가져오기
payload = jwt.decode(access, SECRET_KEY, algorithms=['HS256']) # 디코드하기
pk = payload.get('user_id') # 페이로드에서 user_id 가져오기
user = get_object_or_404(User, pk=pk) # 유저 데이터중에 user_id와 primary key(id)가 같은 게 있는지 확인
serializer = UserSerializer(instance=user) # 시리얼라이즈
return Response(serializer.data, status=status.HTTP_200_OK)
except (jwt.exceptions.InvalidTokenError):
# 사용 불가능한 토큰일 때
return Response(status=status.HTTP_400_BAD_REQUEST)
강의랑 블로그 참고해가면서 했는데 아직 잘 모르겠다.
참고
[DRF] JWT 인증을 사용한 회원가입&로그인
[Django] JWT#2, 장고 JWT토큰 사용하기, payload, Pyjwt, 장고 로그인 유지
https://www.django-rest-framework.org/#installation
https://docs.djangoproject.com/en/4.1/topics/auth/customizing/
https://django-rest-framework-simplejwt.readthedocs.io/en/latest/getting_started.html