From f615e51c5c1069631bc9c36f647bd17cb9212f46 Mon Sep 17 00:00:00 2001 From: Flook Date: Sun, 16 Nov 2025 07:41:33 +0700 Subject: [PATCH] =?UTF-8?q?=E0=B8=9F=E0=B8=B1=E0=B8=87=E0=B8=81=E0=B9=8C?= =?UTF-8?q?=E0=B8=8A=E0=B8=B1=E0=B8=99=20=E0=B8=88=E0=B8=94=E0=B8=88?= =?UTF-8?q?=E0=B8=B3=E0=B8=89=E0=B8=B1=E0=B8=99=20=E0=B8=94=E0=B9=89?= =?UTF-8?q?=E0=B8=A7=E0=B8=A2=20JWT?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/accounts/serializers.py | 36 +++++++++++++++++++++++++++++++++ backend/accounts/views.py | 6 ++++-- backend/core/settings.py | 13 ++++++++++++ backend/core/urls.py | 3 +++ 4 files changed, 56 insertions(+), 2 deletions(-) diff --git a/backend/accounts/serializers.py b/backend/accounts/serializers.py index 28a85d0..e0af2bc 100644 --- a/backend/accounts/serializers.py +++ b/backend/accounts/serializers.py @@ -2,6 +2,10 @@ from djoser.serializers import UserCreateSerializer as BaseUserCreateSerializer from rest_framework import serializers from .models import CustomUser +from rest_framework_simplejwt.serializers import TokenObtainPairSerializer +from rest_framework_simplejwt.tokens import RefreshToken +from django.conf import settings + class UserCreateSerializer(BaseUserCreateSerializer): # Serializer สำหรับการลงทะเบียน (Djoser จะใช้ตัวนี้) class Meta(BaseUserCreateSerializer.Meta): @@ -21,3 +25,35 @@ class UserSerializer(serializers.ModelSerializer): ) # ตั้งค่า is_active, is_staff, is_superuser เป็น read_only read_only_fields = ('id', 'username', 'is_active', 'is_staff', 'is_superuser', 'role') + +# Serializer สำหรับ Login JWT ที่รับค่า remember_me +class CustomTokenObtainPairSerializer(TokenObtainPairSerializer): + + def validate(self, attrs): + # print("CustomTokenObtainPairSerializer called") + data = super().validate(attrs) + + # รับ remember_me จาก request (รองรับ true/false ทั้ง bool และ string) + remember_raw = self.context['request'].data.get('remember_me', False) + + remember_me = ( + remember_raw is True or + str(remember_raw).lower() == "true" or + remember_raw == "1" + ) + + refresh = self.get_token(self.user) + + # ฝัง remember_me ลงใน payload + refresh['remember_me'] = remember_me + + # ถ้า remember_me=True → อายุ Refresh Token เป็น 30 วัน + if remember_me: + refresh.set_exp( + from_time=refresh.current_time, + lifetime=settings.SIMPLE_JWT['REFRESH_TOKEN_LIFETIME_REMEMBER_ME'] + ) + + data['refresh'] = str(refresh) + data['access'] = str(refresh.access_token) + return data diff --git a/backend/accounts/views.py b/backend/accounts/views.py index 91ea44a..d726ef7 100644 --- a/backend/accounts/views.py +++ b/backend/accounts/views.py @@ -1,3 +1,5 @@ -from django.shortcuts import render +from rest_framework_simplejwt.views import TokenObtainPairView +from .serializers import CustomTokenObtainPairSerializer -# Create your views here. +class CustomTokenObtainPairView(TokenObtainPairView): + serializer_class = CustomTokenObtainPairSerializer diff --git a/backend/core/settings.py b/backend/core/settings.py index 1586f70..74d431c 100644 --- a/backend/core/settings.py +++ b/backend/core/settings.py @@ -13,6 +13,8 @@ https://docs.djangoproject.com/en/5.2/ref/settings/ from pathlib import Path import os +from datetime import timedelta + try: from dotenv import load_dotenv load_dotenv() # โหลดตัวแปรจาก .env ใน Local Dev @@ -208,6 +210,17 @@ REST_FRAMEWORK = { } } +SIMPLE_JWT = { + # ชี้ไปที่ Custom Serializer ที่อยู่ใน accounts.serializers + 'TOKEN_OBTAIN_PAIR_SERIALIZER': 'accounts.serializers.CustomTokenObtainPairSerializer', + + 'ACCESS_TOKEN_LIFETIME': timedelta(minutes=15), + 'REFRESH_TOKEN_LIFETIME': timedelta(days=1), + 'REFRESH_TOKEN_LIFETIME_REMEMBER_ME': timedelta(days=30), + + # การตั้งค่าอื่น ๆ ของ SIMPLE_JWT ในอนาคต +} + # 3. ตั้งค่า DJOSER (เพื่อจัดการ Auth Endpoints) DOMAIN = "localhost:5173" SITE_NAME = 'localhost:5173' # หรือชื่อ Domain จริง diff --git a/backend/core/urls.py b/backend/core/urls.py index f465993..68dc096 100644 --- a/backend/core/urls.py +++ b/backend/core/urls.py @@ -25,6 +25,8 @@ from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView, Sp from api.views.health_check_view import SystemHealthCheck from api.views.audit_viewset import AuditLogViewSet +from accounts.views import CustomTokenObtainPairView + # 1. กำหนดตัวแปร router ก่อนใช้งาน router = DefaultRouter() @@ -56,6 +58,7 @@ urlpatterns = [ path('api/schema/redoc/', SpectacularRedocView.as_view(url_name='schema'), name='redoc'), # Endpoints สำหรับการยืนยันตัวตน (Login, Logout, Register) + path("api/v1/auth/jwt/create/", CustomTokenObtainPairView.as_view(), name="jwt-create"), path('api/v1/auth/', include('djoser.urls')), # /users/ (Register/Update/Me), /users/set_password path('api/v1/auth/', include('djoser.urls.jwt')), # /jwt/create (Login), /jwt/refresh (Refresh Token)